diff --git a/Go-Jek/GO-JEK-Assignment/.gitignore b/Go-Jek/GO-JEK-Assignment/.gitignore new file mode 100755 index 0000000..312d1f6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/.gitignore @@ -0,0 +1,68 @@ +# Xcode +# +# gitignore contributors: remember to update Global/Xcode.gitignore, Objective-C.gitignore & Swift.gitignore + +## Build generated +build/ +DerivedData/ + +## Various settings +*.pbxuser +!default.pbxuser +*.mode1v3 +!default.mode1v3 +*.mode2v3 +!default.mode2v3 +*.perspectivev3 +!default.perspectivev3 +xcuserdata/ + +## Other +*.moved-aside +*.xccheckout +*.xcscmblueprint + +## Obj-C/Swift specific +*.hmap +*.ipa +*.dSYM.zip +*.dSYM + +## Playgrounds +timeline.xctimeline +playground.xcworkspace + +# Swift Package Manager +# +# Add this line if you want to avoid checking in source code from Swift Package Manager dependencies. +# Packages/ +# Package.pins +# Package.resolved +.build/ + +# CocoaPods +# +# We recommend against adding the Pods directory to your .gitignore. However +# you should judge for yourself, the pros and cons are mentioned at: +# https://guides.cocoapods.org/using/using-cocoapods.html#should-i-check-the-pods-directory-into-source-control +# +# Pods/ + +# Carthage +# +# Add this line if you want to avoid checking in source code from Carthage dependencies. +# Carthage/Checkouts + +Carthage/Build + +# fastlane +# +# It is recommended to not store the screenshots in the git repo. Instead, use fastlane to re-generate the +# screenshots whenever they are needed. +# For more information about the recommended setup visit: +# https://docs.fastlane.tools/best-practices/source-control/#source-control + +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots/**/*.png +fastlane/test_output diff --git a/Go-Jek/GO-JEK-Assignment/.swiftlint.yml b/Go-Jek/GO-JEK-Assignment/.swiftlint.yml new file mode 100755 index 0000000..110d162 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/.swiftlint.yml @@ -0,0 +1,45 @@ +disabled_rules: +- trailing_whitespace + +opt_in_rules: +- empty_count +- empty_string + +excluded: +- Carthage +- Pods +- SwiftLint/Common/3rdPartyLib + +line_length: + warning: 150 + error: 200 + ignores_function_declarations: true + ignores_comments: true + ignores_urls: true + +function_body_length: + warning: 300 + error: 500 + +function_parameter_count: + warning: 6 + error: 8 + +type_body_length: + warning: 300 + error: 500 + +file_length: + warning: 1000 + error: 1500 + ignore_comment_only_lines: true + +cyclomatic_complexity: + warning: 15 + error: 25 + +identifier_name: + excluded: + - id + +reporter: "xcode" diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.pbxproj b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.pbxproj new file mode 100755 index 0000000..756ea19 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.pbxproj @@ -0,0 +1,1363 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 3FD16D96301F2A6E7788F7FF /* libPods-GJAssignmentUITests.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 0F9A0B487D99F4AB02448EBC /* libPods-GJAssignmentUITests.a */; }; + 44045FCB230705C300571646 /* NSObjectExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FCA230705C300571646 /* NSObjectExtension.swift */; }; + 44045FD923070DF600571646 /* Colors.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 44045FD823070DF600571646 /* Colors.xcassets */; }; + 44045FDB23070F3F00571646 /* UIColorExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FDA23070F3F00571646 /* UIColorExtension.swift */; }; + 44045FE223071FD200571646 /* RequestProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FE123071FD200571646 /* RequestProtocol.swift */; }; + 44045FE82307211800571646 /* HTTPMethod.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FE72307211800571646 /* HTTPMethod.swift */; }; + 44045FEB230721D900571646 /* Log.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FEA230721D900571646 /* Log.swift */; }; + 44045FF3230732DF00571646 /* HTTPClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FF2230732DF00571646 /* HTTPClient.swift */; }; + 44045FF723073D6400571646 /* URLSessionProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FF623073D6400571646 /* URLSessionProtocol.swift */; }; + 44045FF923073DD500571646 /* URLSessionDataTaskProtocol.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FF823073DD500571646 /* URLSessionDataTaskProtocol.swift */; }; + 44045FFD2307BAF900571646 /* URLSessionExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FFC2307BAF900571646 /* URLSessionExtension.swift */; }; + 44045FFF2307BB1300571646 /* URLSessionDataTaskExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44045FFE2307BB1300571646 /* URLSessionDataTaskExtension.swift */; }; + 440460012307BB8200571646 /* URLResponseExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440460002307BB8200571646 /* URLResponseExtension.swift */; }; + 440460052307BD6600571646 /* ContactService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440460042307BD6600571646 /* ContactService.swift */; }; + 440460082307BFCE00571646 /* NetworkConstants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440460072307BFCE00571646 /* NetworkConstants.swift */; }; + 4404600A2307C05500571646 /* Constants.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440460092307C05500571646 /* Constants.swift */; }; + 440C86022307CBB400B86175 /* Bindable.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86012307CBB400B86175 /* Bindable.swift */; }; + 440C86052307DB7B00B86175 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 440C86042307DB7B00B86175 /* Localizable.strings */; }; + 440C86072307E62100B86175 /* AppDelegateExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86062307E62100B86175 /* AppDelegateExtension.swift */; }; + 440C860B2307E78700B86175 /* ContactListViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86092307E78700B86175 /* ContactListViewController.swift */; }; + 440C860C2307E78700B86175 /* ContactListViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C860A2307E78700B86175 /* ContactListViewController.xib */; }; + 440C860F2307ED5100B86175 /* Contact+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C860D2307ED5100B86175 /* Contact+CoreDataClass.swift */; }; + 440C86102307ED5100B86175 /* Contact+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C860E2307ED5100B86175 /* Contact+CoreDataProperties.swift */; }; + 440C86182307FAF800B86175 /* ContactListViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86172307FAF800B86175 /* ContactListViewControllerExtension.swift */; }; + 440C861D2308008800B86175 /* ContactTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C861B2308008800B86175 /* ContactTableViewCell.swift */; }; + 440C861E2308008800B86175 /* ContactTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C861C2308008800B86175 /* ContactTableViewCell.xib */; }; + 440C8620230819CC00B86175 /* UIImageExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C861F230819CC00B86175 /* UIImageExtension.swift */; }; + 440C862723082C1400B86175 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C862623082C1400B86175 /* CoreDataManager.swift */; }; + 440C862F23082F9200B86175 /* UIImageViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C862E23082F9200B86175 /* UIImageViewExtension.swift */; }; + 440C86342308312800B86175 /* ContactDetailsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86322308312800B86175 /* ContactDetailsViewController.swift */; }; + 440C86352308312800B86175 /* ContactDetailsViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C86332308312800B86175 /* ContactDetailsViewController.xib */; }; + 440C863A2308317100B86175 /* ContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86382308317100B86175 /* ContactViewModel.swift */; }; + 440C863B2308317100B86175 /* ContactListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86392308317100B86175 /* ContactListViewModel.swift */; }; + 440C863E2308321F00B86175 /* ContactDetailsViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C863D2308321F00B86175 /* ContactDetailsViewModel.swift */; }; + 440C86422308423C00B86175 /* CircularImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86412308423B00B86175 /* CircularImageView.swift */; }; + 440C86442308476800B86175 /* GradientView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86432308476800B86175 /* GradientView.swift */; }; + 440C86482308747300B86175 /* ContactDetailsTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86462308747300B86175 /* ContactDetailsTableViewCell.swift */; }; + 440C86492308747300B86175 /* ContactDetailsTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C86472308747300B86175 /* ContactDetailsTableViewCell.xib */; }; + 440C864D2308762900B86175 /* ContactEditTableViewCell.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C864B2308762900B86175 /* ContactEditTableViewCell.swift */; }; + 440C864E2308762900B86175 /* ContactEditTableViewCell.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C864C2308762900B86175 /* ContactEditTableViewCell.xib */; }; + 440C865423087A2300B86175 /* ContactMetaData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C865323087A2300B86175 /* ContactMetaData.swift */; }; + 440C865623087C8200B86175 /* ContactDetailsViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C865523087C8200B86175 /* ContactDetailsViewControllerExtension.swift */; }; + 440C865A23088CFF00B86175 /* AddContactViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C865823088CFF00B86175 /* AddContactViewController.swift */; }; + 440C865B23088CFF00B86175 /* AddContactViewController.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C865923088CFF00B86175 /* AddContactViewController.xib */; }; + 440C865E23088E9D00B86175 /* AddContactViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C865D23088E9D00B86175 /* AddContactViewModel.swift */; }; + 440C86602308915900B86175 /* AddContactViewControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C865F2308915900B86175 /* AddContactViewControllerExtension.swift */; }; + 440C866423092DD200B86175 /* ContactEditTableViewHeader.xib in Resources */ = {isa = PBXBuildFile; fileRef = 440C866323092DD200B86175 /* ContactEditTableViewHeader.xib */; }; + 440C866623092E0100B86175 /* ContactEditTableViewHeader.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C866523092E0100B86175 /* ContactEditTableViewHeader.swift */; }; + 440C86682309329A00B86175 /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86672309329A00B86175 /* ImagePicker.swift */; }; + 440C866A2309528A00B86175 /* BindableTextField.swift in Sources */ = {isa = PBXBuildFile; fileRef = 440C86692309528900B86175 /* BindableTextField.swift */; }; + 44575A2F230976920012A223 /* UIViewExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A2E230976920012A223 /* UIViewExtension.swift */; }; + 44575A3523097C1C0012A223 /* GJError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A3423097C1C0012A223 /* GJError.swift */; }; + 44575A3723097DA00012A223 /* UIAlertControllerExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A3623097DA00012A223 /* UIAlertControllerExtension.swift */; }; + 44575A3923098D8C0012A223 /* ServerError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A3823098D8C0012A223 /* ServerError.swift */; }; + 44575A3C2309B1790012A223 /* MockSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A3B2309B1790012A223 /* MockSession.swift */; }; + 44575A3E2309B1B10012A223 /* MockDataTask.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44575A3D2309B1B10012A223 /* MockDataTask.swift */; }; + 44628A43230A85FB001C3D54 /* UpdateContactTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A42230A85FB001C3D54 /* UpdateContactTests.swift */; }; + 44628A47230A866F001C3D54 /* AddContactTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A44230A866F001C3D54 /* AddContactTests.swift */; }; + 44628A48230A866F001C3D54 /* ContactDetailsTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A45230A866F001C3D54 /* ContactDetailsTests.swift */; }; + 44628A49230A866F001C3D54 /* ContactListTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A46230A866F001C3D54 /* ContactListTests.swift */; }; + 44628A51230A8E1F001C3D54 /* contact.json in Resources */ = {isa = PBXBuildFile; fileRef = 44628A4C230A8E1F001C3D54 /* contact.json */; }; + 44628A52230A8E1F001C3D54 /* fav_contact.json in Resources */ = {isa = PBXBuildFile; fileRef = 44628A4D230A8E1F001C3D54 /* fav_contact.json */; }; + 44628A53230A8E1F001C3D54 /* contacts.json in Resources */ = {isa = PBXBuildFile; fileRef = 44628A4E230A8E1F001C3D54 /* contacts.json */; }; + 44628A54230A8E1F001C3D54 /* MockContact.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A4F230A8E1F001C3D54 /* MockContact.swift */; }; + 44628A55230A8E1F001C3D54 /* MockRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A50230A8E1F001C3D54 /* MockRequest.swift */; }; + 44628A5D230AA3CD001C3D54 /* ContactListNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A5C230AA3CD001C3D54 /* ContactListNavigationTests.swift */; }; + 44628A5F230AA3E7001C3D54 /* ContactDetailsNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A5E230AA3E7001C3D54 /* ContactDetailsNavigationTests.swift */; }; + 44628A61230AA3F5001C3D54 /* AddContactNavigationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A60230AA3F5001C3D54 /* AddContactNavigationTests.swift */; }; + 44628A67230AA9D7001C3D54 /* ImagePickerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A66230AA9D7001C3D54 /* ImagePickerTests.swift */; }; + 44628A6F230ABB22001C3D54 /* AddContactControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A6E230ABB22001C3D54 /* AddContactControllerTests.swift */; }; + 44628A71230AE5F9001C3D54 /* ContactDetailsViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A70230AE5F9001C3D54 /* ContactDetailsViewModelTests.swift */; }; + 44628A73230AE83B001C3D54 /* ContactDetailsControllerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A72230AE83B001C3D54 /* ContactDetailsControllerTests.swift */; }; + 44628A75230AF270001C3D54 /* BadMockSession.swift in Sources */ = {isa = PBXBuildFile; fileRef = 44628A74230AF270001C3D54 /* BadMockSession.swift */; }; + 44628A77230AF337001C3D54 /* not_found.json in Resources */ = {isa = PBXBuildFile; fileRef = 44628A76230AF337001C3D54 /* not_found.json */; }; + 446C1B242303DEEB00FEFF90 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446C1B232303DEEB00FEFF90 /* AppDelegate.swift */; }; + 446C1B2C2303DEEB00FEFF90 /* GJAssignment.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 446C1B2A2303DEEB00FEFF90 /* GJAssignment.xcdatamodeld */; }; + 446C1B2E2303DEEC00FEFF90 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 446C1B2D2303DEEC00FEFF90 /* Assets.xcassets */; }; + 446C1B312303DEEC00FEFF90 /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 446C1B2F2303DEEC00FEFF90 /* LaunchScreen.storyboard */; }; + 446C1B3C2303DEED00FEFF90 /* ContactViewModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 446C1B3B2303DEED00FEFF90 /* ContactViewModelTests.swift */; }; + 446C1B59230412DB00FEFF90 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 446C1B58230412DA00FEFF90 /* .swiftlint.yml */; }; + 449B62E1230AF6AD007B413C /* ContactListErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449B62E0230AF6AD007B413C /* ContactListErrorTests.swift */; }; + 449B62E3230AF89A007B413C /* ContactDetailsErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 449B62E2230AF89A007B413C /* ContactDetailsErrorTests.swift */; }; + 4583709E9845FB64234A7AC1 /* Pods_GJAssignmentTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = E6A678E75B204B3F06C92154 /* Pods_GJAssignmentTests.framework */; }; + B5AE55979E187EE55AFB9040 /* Pods_GJAssignment.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 6B577E478F03C1381975DB77 /* Pods_GJAssignment.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + 446C1B382303DEED00FEFF90 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 446C1B182303DEEB00FEFF90 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 446C1B1F2303DEEB00FEFF90; + remoteInfo = GJAssignment; + }; + 446C1B432303DEED00FEFF90 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 446C1B182303DEEB00FEFF90 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 446C1B1F2303DEEB00FEFF90; + remoteInfo = GJAssignment; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 0F9A0B487D99F4AB02448EBC /* libPods-GJAssignmentUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; path = "libPods-GJAssignmentUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 1927E9BFB34263E36C935DE3 /* Pods-GJAssignment.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignment.release.xcconfig"; path = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment.release.xcconfig"; sourceTree = ""; }; + 44045FCA230705C300571646 /* NSObjectExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSObjectExtension.swift; sourceTree = ""; }; + 44045FD823070DF600571646 /* Colors.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Colors.xcassets; sourceTree = ""; }; + 44045FDA23070F3F00571646 /* UIColorExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIColorExtension.swift; sourceTree = ""; }; + 44045FE123071FD200571646 /* RequestProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RequestProtocol.swift; sourceTree = ""; }; + 44045FE72307211800571646 /* HTTPMethod.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPMethod.swift; sourceTree = ""; }; + 44045FEA230721D900571646 /* Log.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Log.swift; sourceTree = ""; }; + 44045FF2230732DF00571646 /* HTTPClient.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HTTPClient.swift; sourceTree = ""; }; + 44045FF623073D6400571646 /* URLSessionProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionProtocol.swift; sourceTree = ""; }; + 44045FF823073DD500571646 /* URLSessionDataTaskProtocol.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionDataTaskProtocol.swift; sourceTree = ""; }; + 44045FFC2307BAF900571646 /* URLSessionExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionExtension.swift; sourceTree = ""; }; + 44045FFE2307BB1300571646 /* URLSessionDataTaskExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLSessionDataTaskExtension.swift; sourceTree = ""; }; + 440460002307BB8200571646 /* URLResponseExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = URLResponseExtension.swift; sourceTree = ""; }; + 440460042307BD6600571646 /* ContactService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactService.swift; sourceTree = ""; }; + 440460072307BFCE00571646 /* NetworkConstants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkConstants.swift; sourceTree = ""; }; + 440460092307C05500571646 /* Constants.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Constants.swift; sourceTree = ""; }; + 440C86012307CBB400B86175 /* Bindable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Bindable.swift; sourceTree = ""; }; + 440C86042307DB7B00B86175 /* Localizable.strings */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; path = Localizable.strings; sourceTree = ""; }; + 440C86062307E62100B86175 /* AppDelegateExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegateExtension.swift; sourceTree = ""; }; + 440C86092307E78700B86175 /* ContactListViewController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactListViewController.swift; sourceTree = ""; }; + 440C860A2307E78700B86175 /* ContactListViewController.xib */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = file.xib; path = ContactListViewController.xib; sourceTree = ""; }; + 440C860D2307ED5100B86175 /* Contact+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Contact+CoreDataClass.swift"; sourceTree = ""; }; + 440C860E2307ED5100B86175 /* Contact+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Contact+CoreDataProperties.swift"; sourceTree = ""; }; + 440C86172307FAF800B86175 /* ContactListViewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListViewControllerExtension.swift; sourceTree = ""; }; + 440C861B2308008800B86175 /* ContactTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactTableViewCell.swift; sourceTree = ""; }; + 440C861C2308008800B86175 /* ContactTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContactTableViewCell.xib; sourceTree = ""; }; + 440C861F230819CC00B86175 /* UIImageExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageExtension.swift; sourceTree = ""; }; + 440C862623082C1400B86175 /* CoreDataManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = ""; }; + 440C862E23082F9200B86175 /* UIImageViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIImageViewExtension.swift; sourceTree = ""; }; + 440C86322308312800B86175 /* ContactDetailsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsViewController.swift; sourceTree = ""; }; + 440C86332308312800B86175 /* ContactDetailsViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContactDetailsViewController.xib; sourceTree = ""; }; + 440C86382308317100B86175 /* ContactViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactViewModel.swift; sourceTree = ""; }; + 440C86392308317100B86175 /* ContactListViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactListViewModel.swift; sourceTree = ""; }; + 440C863D2308321F00B86175 /* ContactDetailsViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsViewModel.swift; sourceTree = ""; }; + 440C86412308423B00B86175 /* CircularImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CircularImageView.swift; sourceTree = ""; }; + 440C86432308476800B86175 /* GradientView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GradientView.swift; sourceTree = ""; }; + 440C86462308747300B86175 /* ContactDetailsTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsTableViewCell.swift; sourceTree = ""; }; + 440C86472308747300B86175 /* ContactDetailsTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContactDetailsTableViewCell.xib; sourceTree = ""; }; + 440C864B2308762900B86175 /* ContactEditTableViewCell.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactEditTableViewCell.swift; sourceTree = ""; }; + 440C864C2308762900B86175 /* ContactEditTableViewCell.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContactEditTableViewCell.xib; sourceTree = ""; }; + 440C865323087A2300B86175 /* ContactMetaData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactMetaData.swift; sourceTree = ""; }; + 440C865523087C8200B86175 /* ContactDetailsViewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsViewControllerExtension.swift; sourceTree = ""; }; + 440C865823088CFF00B86175 /* AddContactViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactViewController.swift; sourceTree = ""; }; + 440C865923088CFF00B86175 /* AddContactViewController.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = AddContactViewController.xib; sourceTree = ""; }; + 440C865D23088E9D00B86175 /* AddContactViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactViewModel.swift; sourceTree = ""; }; + 440C865F2308915900B86175 /* AddContactViewControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactViewControllerExtension.swift; sourceTree = ""; }; + 440C866323092DD200B86175 /* ContactEditTableViewHeader.xib */ = {isa = PBXFileReference; lastKnownFileType = file.xib; path = ContactEditTableViewHeader.xib; sourceTree = ""; }; + 440C866523092E0100B86175 /* ContactEditTableViewHeader.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactEditTableViewHeader.swift; sourceTree = ""; }; + 440C86672309329A00B86175 /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = ""; }; + 440C86692309528900B86175 /* BindableTextField.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BindableTextField.swift; sourceTree = ""; }; + 44575A2E230976920012A223 /* UIViewExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIViewExtension.swift; sourceTree = ""; }; + 44575A3423097C1C0012A223 /* GJError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GJError.swift; sourceTree = ""; }; + 44575A3623097DA00012A223 /* UIAlertControllerExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UIAlertControllerExtension.swift; sourceTree = ""; }; + 44575A3823098D8C0012A223 /* ServerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServerError.swift; sourceTree = ""; }; + 44575A3B2309B1790012A223 /* MockSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockSession.swift; sourceTree = ""; }; + 44575A3D2309B1B10012A223 /* MockDataTask.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockDataTask.swift; sourceTree = ""; }; + 44628A42230A85FB001C3D54 /* UpdateContactTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = UpdateContactTests.swift; path = GJAssignmentTests/UpdateContactTests/UpdateContactTests.swift; sourceTree = SOURCE_ROOT; }; + 44628A44230A866F001C3D54 /* AddContactTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddContactTests.swift; sourceTree = ""; }; + 44628A45230A866F001C3D54 /* ContactDetailsTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactDetailsTests.swift; sourceTree = ""; }; + 44628A46230A866F001C3D54 /* ContactListTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactListTests.swift; sourceTree = ""; }; + 44628A4C230A8E1F001C3D54 /* contact.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = contact.json; sourceTree = ""; }; + 44628A4D230A8E1F001C3D54 /* fav_contact.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = fav_contact.json; sourceTree = ""; }; + 44628A4E230A8E1F001C3D54 /* contacts.json */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.json; path = contacts.json; sourceTree = ""; }; + 44628A4F230A8E1F001C3D54 /* MockContact.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockContact.swift; sourceTree = ""; }; + 44628A50230A8E1F001C3D54 /* MockRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MockRequest.swift; sourceTree = ""; }; + 44628A5C230AA3CD001C3D54 /* ContactListNavigationTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContactListNavigationTests.swift; sourceTree = ""; }; + 44628A5E230AA3E7001C3D54 /* ContactDetailsNavigationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsNavigationTests.swift; sourceTree = ""; }; + 44628A60230AA3F5001C3D54 /* AddContactNavigationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactNavigationTests.swift; sourceTree = ""; }; + 44628A66230AA9D7001C3D54 /* ImagePickerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePickerTests.swift; sourceTree = ""; }; + 44628A6E230ABB22001C3D54 /* AddContactControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactControllerTests.swift; sourceTree = ""; }; + 44628A70230AE5F9001C3D54 /* ContactDetailsViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsViewModelTests.swift; sourceTree = ""; }; + 44628A72230AE83B001C3D54 /* ContactDetailsControllerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsControllerTests.swift; sourceTree = ""; }; + 44628A74230AF270001C3D54 /* BadMockSession.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BadMockSession.swift; sourceTree = ""; }; + 44628A76230AF337001C3D54 /* not_found.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = not_found.json; sourceTree = ""; }; + 446C1B202303DEEB00FEFF90 /* GJAssignment.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = GJAssignment.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 446C1B232303DEEB00FEFF90 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; + 446C1B2B2303DEEB00FEFF90 /* GJAssignment.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = GJAssignment.xcdatamodel; sourceTree = ""; }; + 446C1B2D2303DEEC00FEFF90 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 446C1B302303DEEC00FEFF90 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; }; + 446C1B322303DEEC00FEFF90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 446C1B372303DEED00FEFF90 /* GJAssignmentTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GJAssignmentTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 446C1B3B2303DEED00FEFF90 /* ContactViewModelTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactViewModelTests.swift; sourceTree = ""; }; + 446C1B3D2303DEED00FEFF90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 446C1B422303DEED00FEFF90 /* GJAssignmentUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = GJAssignmentUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; + 446C1B482303DEED00FEFF90 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 446C1B58230412DA00FEFF90 /* .swiftlint.yml */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = .swiftlint.yml; sourceTree = SOURCE_ROOT; }; + 449B62E0230AF6AD007B413C /* ContactListErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListErrorTests.swift; sourceTree = ""; }; + 449B62E2230AF89A007B413C /* ContactDetailsErrorTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactDetailsErrorTests.swift; sourceTree = ""; }; + 6B577E478F03C1381975DB77 /* Pods_GJAssignment.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GJAssignment.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 85F714077EBAD227A9BEFE18 /* Pods-GJAssignmentUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignmentUITests.debug.xcconfig"; path = "Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.debug.xcconfig"; sourceTree = ""; }; + 8DF2889CB59C03A02ADDF865 /* Pods-GJAssignmentUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignmentUITests.release.xcconfig"; path = "Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.release.xcconfig"; sourceTree = ""; }; + 96D3B9D0C63BF71D93493961 /* Pods-GJAssignmentTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignmentTests.release.xcconfig"; path = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.release.xcconfig"; sourceTree = ""; }; + E6A678E75B204B3F06C92154 /* Pods_GJAssignmentTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_GJAssignmentTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + FAC22195A9E4BFE8E93E73E6 /* Pods-GJAssignmentTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignmentTests.debug.xcconfig"; path = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.debug.xcconfig"; sourceTree = ""; }; + FACDF7DDA12FF8B0A97F4283 /* Pods-GJAssignment.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-GJAssignment.debug.xcconfig"; path = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment.debug.xcconfig"; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 446C1B1D2303DEEB00FEFF90 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + B5AE55979E187EE55AFB9040 /* Pods_GJAssignment.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B342303DEED00FEFF90 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 4583709E9845FB64234A7AC1 /* Pods_GJAssignmentTests.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B3F2303DEED00FEFF90 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 3FD16D96301F2A6E7788F7FF /* libPods-GJAssignmentUITests.a in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 44045FBE2306FB5700571646 /* Controllers */ = { + isa = PBXGroup; + children = ( + 440C86082307E78700B86175 /* ContactListViewController */, + 440C86312308310700B86175 /* ContactDetailsViewController */, + 440C865723088CB600B86175 /* AddContactViewController */, + ); + path = Controllers; + sourceTree = ""; + }; + 44045FBF2306FB7000571646 /* Models */ = { + isa = PBXGroup; + children = ( + 440C860D2307ED5100B86175 /* Contact+CoreDataClass.swift */, + 440C860E2307ED5100B86175 /* Contact+CoreDataProperties.swift */, + 440C865323087A2300B86175 /* ContactMetaData.swift */, + 44575A3823098D8C0012A223 /* ServerError.swift */, + ); + path = Models; + sourceTree = ""; + }; + 44045FC02306FB7500571646 /* ViewModels */ = { + isa = PBXGroup; + children = ( + 440C86372308317100B86175 /* ContactListViewModel */, + 440C863C2308318A00B86175 /* ContactDetailsViewModel */, + 440C865C23088E8100B86175 /* AddContactViewModel */, + ); + path = ViewModels; + sourceTree = ""; + }; + 44045FC12306FD0300571646 /* Views */ = { + isa = PBXGroup; + children = ( + 440C861A2308006900B86175 /* ContactTableViewCell */, + 440C864A230874D200B86175 /* ContactEditTableViewCell */, + 440C86452308743D00B86175 /* ContactDetailsTableViewCell */, + 440C866223092DB400B86175 /* ContactEditTableViewHeader */, + ); + path = Views; + sourceTree = ""; + }; + 44045FC62307056400571646 /* Utilities */ = { + isa = PBXGroup; + children = ( + 440C862523082C1400B86175 /* CoreDataManager */, + 440C86002307CBA700B86175 /* Bindable */, + 44045FE92307216100571646 /* Logger */, + 44045FC72307057B00571646 /* Extensions */, + 440C86402308420900B86175 /* CustomControls */, + 440460092307C05500571646 /* Constants.swift */, + ); + path = Utilities; + sourceTree = ""; + }; + 44045FC72307057B00571646 /* Extensions */ = { + isa = PBXGroup; + children = ( + 44045FCA230705C300571646 /* NSObjectExtension.swift */, + 44045FDA23070F3F00571646 /* UIColorExtension.swift */, + 440C861F230819CC00B86175 /* UIImageExtension.swift */, + 440C862E23082F9200B86175 /* UIImageViewExtension.swift */, + 44575A2E230976920012A223 /* UIViewExtension.swift */, + 44575A3623097DA00012A223 /* UIAlertControllerExtension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 44045FD523070B6000571646 /* Resources */ = { + isa = PBXGroup; + children = ( + 44045FD823070DF600571646 /* Colors.xcassets */, + 446C1B2D2303DEEC00FEFF90 /* Assets.xcassets */, + 440C86042307DB7B00B86175 /* Localizable.strings */, + ); + path = Resources; + sourceTree = ""; + }; + 44045FE023071F2300571646 /* Networking */ = { + isa = PBXGroup; + children = ( + 440460062307BFBF00571646 /* Constants */, + 44045FFA2307BACC00571646 /* Protocols */, + 44045FFB2307BAE200571646 /* Extensions */, + 440460022307BD3900571646 /* HTTPClient */, + 440460032307BD4A00571646 /* AppServices */, + ); + path = Networking; + sourceTree = ""; + }; + 44045FE92307216100571646 /* Logger */ = { + isa = PBXGroup; + children = ( + 44045FEA230721D900571646 /* Log.swift */, + ); + path = Logger; + sourceTree = ""; + }; + 44045FFA2307BACC00571646 /* Protocols */ = { + isa = PBXGroup; + children = ( + 44045FE123071FD200571646 /* RequestProtocol.swift */, + 44045FF623073D6400571646 /* URLSessionProtocol.swift */, + 44045FF823073DD500571646 /* URLSessionDataTaskProtocol.swift */, + ); + path = Protocols; + sourceTree = ""; + }; + 44045FFB2307BAE200571646 /* Extensions */ = { + isa = PBXGroup; + children = ( + 44045FFC2307BAF900571646 /* URLSessionExtension.swift */, + 44045FFE2307BB1300571646 /* URLSessionDataTaskExtension.swift */, + 440460002307BB8200571646 /* URLResponseExtension.swift */, + ); + path = Extensions; + sourceTree = ""; + }; + 440460022307BD3900571646 /* HTTPClient */ = { + isa = PBXGroup; + children = ( + 44575A3423097C1C0012A223 /* GJError.swift */, + 44045FE72307211800571646 /* HTTPMethod.swift */, + 44045FF2230732DF00571646 /* HTTPClient.swift */, + ); + path = HTTPClient; + sourceTree = ""; + }; + 440460032307BD4A00571646 /* AppServices */ = { + isa = PBXGroup; + children = ( + 440460042307BD6600571646 /* ContactService.swift */, + ); + path = AppServices; + sourceTree = ""; + }; + 440460062307BFBF00571646 /* Constants */ = { + isa = PBXGroup; + children = ( + 440460072307BFCE00571646 /* NetworkConstants.swift */, + ); + path = Constants; + sourceTree = ""; + }; + 440C86002307CBA700B86175 /* Bindable */ = { + isa = PBXGroup; + children = ( + 440C86012307CBB400B86175 /* Bindable.swift */, + 440C86692309528900B86175 /* BindableTextField.swift */, + ); + path = Bindable; + sourceTree = ""; + }; + 440C86082307E78700B86175 /* ContactListViewController */ = { + isa = PBXGroup; + children = ( + 440C86092307E78700B86175 /* ContactListViewController.swift */, + 440C86172307FAF800B86175 /* ContactListViewControllerExtension.swift */, + 440C860A2307E78700B86175 /* ContactListViewController.xib */, + ); + path = ContactListViewController; + sourceTree = ""; + }; + 440C861A2308006900B86175 /* ContactTableViewCell */ = { + isa = PBXGroup; + children = ( + 440C861B2308008800B86175 /* ContactTableViewCell.swift */, + 440C861C2308008800B86175 /* ContactTableViewCell.xib */, + ); + path = ContactTableViewCell; + sourceTree = ""; + }; + 440C862523082C1400B86175 /* CoreDataManager */ = { + isa = PBXGroup; + children = ( + 440C862623082C1400B86175 /* CoreDataManager.swift */, + ); + path = CoreDataManager; + sourceTree = ""; + }; + 440C86312308310700B86175 /* ContactDetailsViewController */ = { + isa = PBXGroup; + children = ( + 440C86322308312800B86175 /* ContactDetailsViewController.swift */, + 440C865523087C8200B86175 /* ContactDetailsViewControllerExtension.swift */, + 440C86332308312800B86175 /* ContactDetailsViewController.xib */, + ); + path = ContactDetailsViewController; + sourceTree = ""; + }; + 440C86372308317100B86175 /* ContactListViewModel */ = { + isa = PBXGroup; + children = ( + 440C86382308317100B86175 /* ContactViewModel.swift */, + 440C86392308317100B86175 /* ContactListViewModel.swift */, + ); + path = ContactListViewModel; + sourceTree = ""; + }; + 440C863C2308318A00B86175 /* ContactDetailsViewModel */ = { + isa = PBXGroup; + children = ( + 440C863D2308321F00B86175 /* ContactDetailsViewModel.swift */, + ); + path = ContactDetailsViewModel; + sourceTree = ""; + }; + 440C86402308420900B86175 /* CustomControls */ = { + isa = PBXGroup; + children = ( + 440C86412308423B00B86175 /* CircularImageView.swift */, + 440C86432308476800B86175 /* GradientView.swift */, + 440C86672309329A00B86175 /* ImagePicker.swift */, + ); + path = CustomControls; + sourceTree = ""; + }; + 440C86452308743D00B86175 /* ContactDetailsTableViewCell */ = { + isa = PBXGroup; + children = ( + 440C86462308747300B86175 /* ContactDetailsTableViewCell.swift */, + 440C86472308747300B86175 /* ContactDetailsTableViewCell.xib */, + ); + path = ContactDetailsTableViewCell; + sourceTree = ""; + }; + 440C864A230874D200B86175 /* ContactEditTableViewCell */ = { + isa = PBXGroup; + children = ( + 440C864B2308762900B86175 /* ContactEditTableViewCell.swift */, + 440C864C2308762900B86175 /* ContactEditTableViewCell.xib */, + ); + path = ContactEditTableViewCell; + sourceTree = ""; + }; + 440C865723088CB600B86175 /* AddContactViewController */ = { + isa = PBXGroup; + children = ( + 440C865823088CFF00B86175 /* AddContactViewController.swift */, + 440C865F2308915900B86175 /* AddContactViewControllerExtension.swift */, + 440C865923088CFF00B86175 /* AddContactViewController.xib */, + ); + path = AddContactViewController; + sourceTree = ""; + }; + 440C865C23088E8100B86175 /* AddContactViewModel */ = { + isa = PBXGroup; + children = ( + 440C865D23088E9D00B86175 /* AddContactViewModel.swift */, + ); + path = AddContactViewModel; + sourceTree = ""; + }; + 440C866223092DB400B86175 /* ContactEditTableViewHeader */ = { + isa = PBXGroup; + children = ( + 440C866523092E0100B86175 /* ContactEditTableViewHeader.swift */, + 440C866323092DD200B86175 /* ContactEditTableViewHeader.xib */, + ); + path = ContactEditTableViewHeader; + sourceTree = ""; + }; + 44575A3A2309B1690012A223 /* MockNetworking */ = { + isa = PBXGroup; + children = ( + 44575A3B2309B1790012A223 /* MockSession.swift */, + 44628A74230AF270001C3D54 /* BadMockSession.swift */, + 44575A3D2309B1B10012A223 /* MockDataTask.swift */, + ); + path = MockNetworking; + sourceTree = ""; + }; + 44628A4A230A8E1F001C3D54 /* MockData */ = { + isa = PBXGroup; + children = ( + 44628A4B230A8E1F001C3D54 /* StubJSON */, + 44628A4F230A8E1F001C3D54 /* MockContact.swift */, + 44628A50230A8E1F001C3D54 /* MockRequest.swift */, + ); + path = MockData; + sourceTree = ""; + }; + 44628A4B230A8E1F001C3D54 /* StubJSON */ = { + isa = PBXGroup; + children = ( + 44628A4C230A8E1F001C3D54 /* contact.json */, + 44628A4D230A8E1F001C3D54 /* fav_contact.json */, + 44628A4E230A8E1F001C3D54 /* contacts.json */, + 44628A76230AF337001C3D54 /* not_found.json */, + ); + path = StubJSON; + sourceTree = ""; + }; + 44628A59230AA3AE001C3D54 /* NavigationTests */ = { + isa = PBXGroup; + children = ( + 44628A5C230AA3CD001C3D54 /* ContactListNavigationTests.swift */, + 44628A5E230AA3E7001C3D54 /* ContactDetailsNavigationTests.swift */, + 44628A60230AA3F5001C3D54 /* AddContactNavigationTests.swift */, + ); + path = NavigationTests; + sourceTree = ""; + }; + 44628A65230AA9C8001C3D54 /* CustomControlTests */ = { + isa = PBXGroup; + children = ( + 44628A66230AA9D7001C3D54 /* ImagePickerTests.swift */, + ); + path = CustomControlTests; + sourceTree = ""; + }; + 44628A6D230ABA4C001C3D54 /* ViewControllerTests */ = { + isa = PBXGroup; + children = ( + 44628A6E230ABB22001C3D54 /* AddContactControllerTests.swift */, + 44628A72230AE83B001C3D54 /* ContactDetailsControllerTests.swift */, + ); + path = ViewControllerTests; + sourceTree = ""; + }; + 446C1B172303DEEB00FEFF90 = { + isa = PBXGroup; + children = ( + 446C1B222303DEEB00FEFF90 /* GJAssignment */, + 446C1B3A2303DEED00FEFF90 /* GJAssignmentTests */, + 446C1B452303DEED00FEFF90 /* GJAssignmentUITests */, + 446C1B212303DEEB00FEFF90 /* Products */, + ED9B1725C7D33D8092019850 /* Pods */, + AAEE4643F04EF12076E50DAA /* Frameworks */, + ); + sourceTree = ""; + }; + 446C1B212303DEEB00FEFF90 /* Products */ = { + isa = PBXGroup; + children = ( + 446C1B202303DEEB00FEFF90 /* GJAssignment.app */, + 446C1B372303DEED00FEFF90 /* GJAssignmentTests.xctest */, + 446C1B422303DEED00FEFF90 /* GJAssignmentUITests.xctest */, + ); + name = Products; + sourceTree = ""; + }; + 446C1B222303DEEB00FEFF90 /* GJAssignment */ = { + isa = PBXGroup; + children = ( + 44045FC62307056400571646 /* Utilities */, + 44045FE023071F2300571646 /* Networking */, + 44045FBF2306FB7000571646 /* Models */, + 44045FC12306FD0300571646 /* Views */, + 44045FC02306FB7500571646 /* ViewModels */, + 44045FBE2306FB5700571646 /* Controllers */, + 446C1B5C230423B000FEFF90 /* AppDelegate */, + 44045FD523070B6000571646 /* Resources */, + 446C1B572304126000FEFF90 /* SupportingFiles */, + 446C1B2A2303DEEB00FEFF90 /* GJAssignment.xcdatamodeld */, + ); + path = GJAssignment; + sourceTree = ""; + }; + 446C1B3A2303DEED00FEFF90 /* GJAssignmentTests */ = { + isa = PBXGroup; + children = ( + 44628A4A230A8E1F001C3D54 /* MockData */, + 44575A3A2309B1690012A223 /* MockNetworking */, + 449B62DC230AF57B007B413C /* ContactListTests */, + 449B62DD230AF590007B413C /* ContactDetailsTests */, + 449B62DE230AF5B1007B413C /* AddContactTests */, + 449B62DF230AF5BF007B413C /* UpdateContactTests */, + 446C1B3D2303DEED00FEFF90 /* Info.plist */, + ); + path = GJAssignmentTests; + sourceTree = ""; + }; + 446C1B452303DEED00FEFF90 /* GJAssignmentUITests */ = { + isa = PBXGroup; + children = ( + 44628A59230AA3AE001C3D54 /* NavigationTests */, + 44628A6D230ABA4C001C3D54 /* ViewControllerTests */, + 44628A65230AA9C8001C3D54 /* CustomControlTests */, + 446C1B482303DEED00FEFF90 /* Info.plist */, + ); + path = GJAssignmentUITests; + sourceTree = ""; + }; + 446C1B572304126000FEFF90 /* SupportingFiles */ = { + isa = PBXGroup; + children = ( + 446C1B2F2303DEEC00FEFF90 /* LaunchScreen.storyboard */, + 446C1B322303DEEC00FEFF90 /* Info.plist */, + 446C1B58230412DA00FEFF90 /* .swiftlint.yml */, + ); + name = SupportingFiles; + sourceTree = ""; + }; + 446C1B5C230423B000FEFF90 /* AppDelegate */ = { + isa = PBXGroup; + children = ( + 446C1B232303DEEB00FEFF90 /* AppDelegate.swift */, + 440C86062307E62100B86175 /* AppDelegateExtension.swift */, + ); + path = AppDelegate; + sourceTree = ""; + }; + 449B62DC230AF57B007B413C /* ContactListTests */ = { + isa = PBXGroup; + children = ( + 44628A46230A866F001C3D54 /* ContactListTests.swift */, + 449B62E0230AF6AD007B413C /* ContactListErrorTests.swift */, + 446C1B3B2303DEED00FEFF90 /* ContactViewModelTests.swift */, + ); + path = ContactListTests; + sourceTree = ""; + }; + 449B62DD230AF590007B413C /* ContactDetailsTests */ = { + isa = PBXGroup; + children = ( + 44628A45230A866F001C3D54 /* ContactDetailsTests.swift */, + 449B62E2230AF89A007B413C /* ContactDetailsErrorTests.swift */, + 44628A70230AE5F9001C3D54 /* ContactDetailsViewModelTests.swift */, + ); + path = ContactDetailsTests; + sourceTree = ""; + }; + 449B62DE230AF5B1007B413C /* AddContactTests */ = { + isa = PBXGroup; + children = ( + 44628A44230A866F001C3D54 /* AddContactTests.swift */, + ); + path = AddContactTests; + sourceTree = ""; + }; + 449B62DF230AF5BF007B413C /* UpdateContactTests */ = { + isa = PBXGroup; + children = ( + 44628A42230A85FB001C3D54 /* UpdateContactTests.swift */, + ); + path = UpdateContactTests; + sourceTree = ""; + }; + AAEE4643F04EF12076E50DAA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 6B577E478F03C1381975DB77 /* Pods_GJAssignment.framework */, + E6A678E75B204B3F06C92154 /* Pods_GJAssignmentTests.framework */, + 0F9A0B487D99F4AB02448EBC /* libPods-GJAssignmentUITests.a */, + ); + name = Frameworks; + sourceTree = ""; + }; + ED9B1725C7D33D8092019850 /* Pods */ = { + isa = PBXGroup; + children = ( + FACDF7DDA12FF8B0A97F4283 /* Pods-GJAssignment.debug.xcconfig */, + 1927E9BFB34263E36C935DE3 /* Pods-GJAssignment.release.xcconfig */, + FAC22195A9E4BFE8E93E73E6 /* Pods-GJAssignmentTests.debug.xcconfig */, + 96D3B9D0C63BF71D93493961 /* Pods-GJAssignmentTests.release.xcconfig */, + 85F714077EBAD227A9BEFE18 /* Pods-GJAssignmentUITests.debug.xcconfig */, + 8DF2889CB59C03A02ADDF865 /* Pods-GJAssignmentUITests.release.xcconfig */, + ); + path = Pods; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + 446C1B1F2303DEEB00FEFF90 /* GJAssignment */ = { + isa = PBXNativeTarget; + buildConfigurationList = 446C1B4B2303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignment" */; + buildPhases = ( + 117956759E5064D0EEE07BA9 /* [CP] Check Pods Manifest.lock */, + 446C1B1C2303DEEB00FEFF90 /* Sources */, + 446C1B1D2303DEEB00FEFF90 /* Frameworks */, + 446C1B1E2303DEEB00FEFF90 /* Resources */, + 446C1B542304120800FEFF90 /* SwiftLint Run Script */, + 10F3C27B978BDACB77F3B83F /* [CP] Embed Pods Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = GJAssignment; + productName = GJAssignment; + productReference = 446C1B202303DEEB00FEFF90 /* GJAssignment.app */; + productType = "com.apple.product-type.application"; + }; + 446C1B362303DEED00FEFF90 /* GJAssignmentTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 446C1B4E2303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignmentTests" */; + buildPhases = ( + CBB374A1D4D45EE3EE209939 /* [CP] Check Pods Manifest.lock */, + 446C1B332303DEED00FEFF90 /* Sources */, + 446C1B342303DEED00FEFF90 /* Frameworks */, + 446C1B352303DEED00FEFF90 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 446C1B392303DEED00FEFF90 /* PBXTargetDependency */, + ); + name = GJAssignmentTests; + productName = GJAssignmentTests; + productReference = 446C1B372303DEED00FEFF90 /* GJAssignmentTests.xctest */; + productType = "com.apple.product-type.bundle.unit-test"; + }; + 446C1B412303DEED00FEFF90 /* GJAssignmentUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = 446C1B512303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignmentUITests" */; + buildPhases = ( + 0E874002A37B6343E8535938 /* [CP] Check Pods Manifest.lock */, + 446C1B3E2303DEED00FEFF90 /* Sources */, + 446C1B3F2303DEED00FEFF90 /* Frameworks */, + 446C1B402303DEED00FEFF90 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 446C1B442303DEED00FEFF90 /* PBXTargetDependency */, + ); + name = GJAssignmentUITests; + productName = GJAssignmentUITests; + productReference = 446C1B422303DEED00FEFF90 /* GJAssignmentUITests.xctest */; + productType = "com.apple.product-type.bundle.ui-testing"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + 446C1B182303DEEB00FEFF90 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1030; + LastUpgradeCheck = 1030; + ORGANIZATIONNAME = "Anonymous"; + TargetAttributes = { + 446C1B1F2303DEEB00FEFF90 = { + CreatedOnToolsVersion = 10.3; + }; + 446C1B362303DEED00FEFF90 = { + CreatedOnToolsVersion = 10.3; + TestTargetID = 446C1B1F2303DEEB00FEFF90; + }; + 446C1B412303DEED00FEFF90 = { + CreatedOnToolsVersion = 10.3; + TestTargetID = 446C1B1F2303DEEB00FEFF90; + }; + }; + }; + buildConfigurationList = 446C1B1B2303DEEB00FEFF90 /* Build configuration list for PBXProject "GJAssignment" */; + compatibilityVersion = "Xcode 9.3"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = 446C1B172303DEEB00FEFF90; + productRefGroup = 446C1B212303DEEB00FEFF90 /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 446C1B1F2303DEEB00FEFF90 /* GJAssignment */, + 446C1B362303DEED00FEFF90 /* GJAssignmentTests */, + 446C1B412303DEED00FEFF90 /* GJAssignmentUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 446C1B1E2303DEEB00FEFF90 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 446C1B59230412DB00FEFF90 /* .swiftlint.yml in Resources */, + 440C860C2307E78700B86175 /* ContactListViewController.xib in Resources */, + 440C861E2308008800B86175 /* ContactTableViewCell.xib in Resources */, + 440C86052307DB7B00B86175 /* Localizable.strings in Resources */, + 440C86352308312800B86175 /* ContactDetailsViewController.xib in Resources */, + 44045FD923070DF600571646 /* Colors.xcassets in Resources */, + 446C1B312303DEEC00FEFF90 /* LaunchScreen.storyboard in Resources */, + 440C866423092DD200B86175 /* ContactEditTableViewHeader.xib in Resources */, + 440C865B23088CFF00B86175 /* AddContactViewController.xib in Resources */, + 440C86492308747300B86175 /* ContactDetailsTableViewCell.xib in Resources */, + 440C864E2308762900B86175 /* ContactEditTableViewCell.xib in Resources */, + 446C1B2E2303DEEC00FEFF90 /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B352303DEED00FEFF90 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 44628A51230A8E1F001C3D54 /* contact.json in Resources */, + 44628A52230A8E1F001C3D54 /* fav_contact.json in Resources */, + 44628A77230AF337001C3D54 /* not_found.json in Resources */, + 44628A53230A8E1F001C3D54 /* contacts.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B402303DEED00FEFF90 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXShellScriptBuildPhase section */ + 0E874002A37B6343E8535938 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-GJAssignmentUITests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 10F3C27B978BDACB77F3B83F /* [CP] Embed Pods Frameworks */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-${CONFIGURATION}-input-files.xcfilelist", + ); + name = "[CP] Embed Pods Frameworks"; + outputFileListPaths = ( + "${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-${CONFIGURATION}-output-files.xcfilelist", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh\"\n"; + showEnvVarsInLog = 0; + }; + 117956759E5064D0EEE07BA9 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-GJAssignment-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; + 446C1B542304120800FEFF90 /* SwiftLint Run Script */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "SwiftLint Run Script"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "if which swiftlint >/dev/null; then\nswiftlint\nelse\necho \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n"; + }; + CBB374A1D4D45EE3EE209939 /* [CP] Check Pods Manifest.lock */ = { + isa = PBXShellScriptBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", + "${PODS_ROOT}/Manifest.lock", + ); + name = "[CP] Check Pods Manifest.lock"; + outputFileListPaths = ( + ); + outputPaths = ( + "$(DERIVED_FILE_DIR)/Pods-GJAssignmentTests-checkManifestLockResult.txt", + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; + showEnvVarsInLog = 0; + }; +/* End PBXShellScriptBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 446C1B1C2303DEEB00FEFF90 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 440C861D2308008800B86175 /* ContactTableViewCell.swift in Sources */, + 44045FF723073D6400571646 /* URLSessionProtocol.swift in Sources */, + 440460082307BFCE00571646 /* NetworkConstants.swift in Sources */, + 440C86602308915900B86175 /* AddContactViewControllerExtension.swift in Sources */, + 446C1B2C2303DEEB00FEFF90 /* GJAssignment.xcdatamodeld in Sources */, + 440C863B2308317100B86175 /* ContactListViewModel.swift in Sources */, + 440C8620230819CC00B86175 /* UIImageExtension.swift in Sources */, + 440C86422308423C00B86175 /* CircularImageView.swift in Sources */, + 440C86182307FAF800B86175 /* ContactListViewControllerExtension.swift in Sources */, + 440C863A2308317100B86175 /* ContactViewModel.swift in Sources */, + 440C86482308747300B86175 /* ContactDetailsTableViewCell.swift in Sources */, + 440C865A23088CFF00B86175 /* AddContactViewController.swift in Sources */, + 440C865E23088E9D00B86175 /* AddContactViewModel.swift in Sources */, + 440460012307BB8200571646 /* URLResponseExtension.swift in Sources */, + 440C865423087A2300B86175 /* ContactMetaData.swift in Sources */, + 44575A3723097DA00012A223 /* UIAlertControllerExtension.swift in Sources */, + 44045FFD2307BAF900571646 /* URLSessionExtension.swift in Sources */, + 44575A2F230976920012A223 /* UIViewExtension.swift in Sources */, + 44045FF923073DD500571646 /* URLSessionDataTaskProtocol.swift in Sources */, + 440C86682309329A00B86175 /* ImagePicker.swift in Sources */, + 440C86022307CBB400B86175 /* Bindable.swift in Sources */, + 440C866623092E0100B86175 /* ContactEditTableViewHeader.swift in Sources */, + 440C866A2309528A00B86175 /* BindableTextField.swift in Sources */, + 44575A3923098D8C0012A223 /* ServerError.swift in Sources */, + 44045FEB230721D900571646 /* Log.swift in Sources */, + 44045FF3230732DF00571646 /* HTTPClient.swift in Sources */, + 440C863E2308321F00B86175 /* ContactDetailsViewModel.swift in Sources */, + 440C860F2307ED5100B86175 /* Contact+CoreDataClass.swift in Sources */, + 440C86102307ED5100B86175 /* Contact+CoreDataProperties.swift in Sources */, + 44045FFF2307BB1300571646 /* URLSessionDataTaskExtension.swift in Sources */, + 44045FCB230705C300571646 /* NSObjectExtension.swift in Sources */, + 446C1B242303DEEB00FEFF90 /* AppDelegate.swift in Sources */, + 44045FE82307211800571646 /* HTTPMethod.swift in Sources */, + 440C86442308476800B86175 /* GradientView.swift in Sources */, + 440C864D2308762900B86175 /* ContactEditTableViewCell.swift in Sources */, + 440460052307BD6600571646 /* ContactService.swift in Sources */, + 44045FE223071FD200571646 /* RequestProtocol.swift in Sources */, + 44575A3523097C1C0012A223 /* GJError.swift in Sources */, + 440C865623087C8200B86175 /* ContactDetailsViewControllerExtension.swift in Sources */, + 440C862F23082F9200B86175 /* UIImageViewExtension.swift in Sources */, + 44045FDB23070F3F00571646 /* UIColorExtension.swift in Sources */, + 440C862723082C1400B86175 /* CoreDataManager.swift in Sources */, + 440C86072307E62100B86175 /* AppDelegateExtension.swift in Sources */, + 4404600A2307C05500571646 /* Constants.swift in Sources */, + 440C86342308312800B86175 /* ContactDetailsViewController.swift in Sources */, + 440C860B2307E78700B86175 /* ContactListViewController.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B332303DEED00FEFF90 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 44628A55230A8E1F001C3D54 /* MockRequest.swift in Sources */, + 44628A47230A866F001C3D54 /* AddContactTests.swift in Sources */, + 44628A49230A866F001C3D54 /* ContactListTests.swift in Sources */, + 44575A3E2309B1B10012A223 /* MockDataTask.swift in Sources */, + 44628A48230A866F001C3D54 /* ContactDetailsTests.swift in Sources */, + 44628A43230A85FB001C3D54 /* UpdateContactTests.swift in Sources */, + 44628A71230AE5F9001C3D54 /* ContactDetailsViewModelTests.swift in Sources */, + 449B62E1230AF6AD007B413C /* ContactListErrorTests.swift in Sources */, + 44628A54230A8E1F001C3D54 /* MockContact.swift in Sources */, + 44575A3C2309B1790012A223 /* MockSession.swift in Sources */, + 449B62E3230AF89A007B413C /* ContactDetailsErrorTests.swift in Sources */, + 44628A75230AF270001C3D54 /* BadMockSession.swift in Sources */, + 446C1B3C2303DEED00FEFF90 /* ContactViewModelTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 446C1B3E2303DEED00FEFF90 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 44628A5D230AA3CD001C3D54 /* ContactListNavigationTests.swift in Sources */, + 44628A73230AE83B001C3D54 /* ContactDetailsControllerTests.swift in Sources */, + 44628A61230AA3F5001C3D54 /* AddContactNavigationTests.swift in Sources */, + 44628A67230AA9D7001C3D54 /* ImagePickerTests.swift in Sources */, + 44628A5F230AA3E7001C3D54 /* ContactDetailsNavigationTests.swift in Sources */, + 44628A6F230ABB22001C3D54 /* AddContactControllerTests.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 446C1B392303DEED00FEFF90 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 446C1B1F2303DEEB00FEFF90 /* GJAssignment */; + targetProxy = 446C1B382303DEED00FEFF90 /* PBXContainerItemProxy */; + }; + 446C1B442303DEED00FEFF90 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 446C1B1F2303DEEB00FEFF90 /* GJAssignment */; + targetProxy = 446C1B432303DEED00FEFF90 /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin PBXVariantGroup section */ + 446C1B2F2303DEEC00FEFF90 /* LaunchScreen.storyboard */ = { + isa = PBXVariantGroup; + children = ( + 446C1B302303DEEC00FEFF90 /* Base */, + ); + name = LaunchScreen.storyboard; + sourceTree = ""; + }; +/* End PBXVariantGroup section */ + +/* Begin XCBuildConfiguration section */ + 446C1B492303DEED00FEFF90 /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + 446C1B4A2303DEED00FEFF90 /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + CODE_SIGN_IDENTITY = "iPhone Developer"; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 12.4; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + 446C1B4C2303DEED00FEFF90 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FACDF7DDA12FF8B0A97F4283 /* Pods-GJAssignment.debug.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignment/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignment; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 446C1B4D2303DEED00FEFF90 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 1927E9BFB34263E36C935DE3 /* Pods-GJAssignment.release.xcconfig */; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignment/Info.plist; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignment; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; + 446C1B4F2303DEED00FEFF90 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = FAC22195A9E4BFE8E93E73E6 /* Pods-GJAssignmentTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignmentTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignmentTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GJAssignment.app/GJAssignment"; + }; + name = Debug; + }; + 446C1B502303DEED00FEFF90 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 96D3B9D0C63BF71D93493961 /* Pods-GJAssignmentTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + BUNDLE_LOADER = "$(TEST_HOST)"; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignmentTests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignmentTests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/GJAssignment.app/GJAssignment"; + }; + name = Release; + }; + 446C1B522303DEED00FEFF90 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 85F714077EBAD227A9BEFE18 /* Pods-GJAssignmentUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignmentUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignmentUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = GJAssignment; + }; + name = Debug; + }; + 446C1B532303DEED00FEFF90 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 8DF2889CB59C03A02ADDF865 /* Pods-GJAssignmentUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; + CODE_SIGN_STYLE = Automatic; + DEVELOPMENT_TEAM = AFDKURH36K; + INFOPLIST_FILE = GJAssignmentUITests/Info.plist; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + PRODUCT_BUNDLE_IDENTIFIER = co.developerinsider.GJAssignmentUITests; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + TEST_TARGET_NAME = GJAssignment; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 446C1B1B2303DEEB00FEFF90 /* Build configuration list for PBXProject "GJAssignment" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 446C1B492303DEED00FEFF90 /* Debug */, + 446C1B4A2303DEED00FEFF90 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 446C1B4B2303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignment" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 446C1B4C2303DEED00FEFF90 /* Debug */, + 446C1B4D2303DEED00FEFF90 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 446C1B4E2303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignmentTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 446C1B4F2303DEED00FEFF90 /* Debug */, + 446C1B502303DEED00FEFF90 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 446C1B512303DEED00FEFF90 /* Build configuration list for PBXNativeTarget "GJAssignmentUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 446C1B522303DEED00FEFF90 /* Debug */, + 446C1B532303DEED00FEFF90 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCVersionGroup section */ + 446C1B2A2303DEEB00FEFF90 /* GJAssignment.xcdatamodeld */ = { + isa = XCVersionGroup; + children = ( + 446C1B2B2303DEEB00FEFF90 /* GJAssignment.xcdatamodel */, + ); + currentVersion = 446C1B2B2303DEEB00FEFF90 /* GJAssignment.xcdatamodel */; + path = GJAssignment.xcdatamodeld; + sourceTree = ""; + versionGroupType = wrapper.xcdatamodel; + }; +/* End XCVersionGroup section */ + }; + rootObject = 446C1B182303DEEB00FEFF90 /* Project object */; +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100755 index 0000000..a40fd58 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100755 index 0000000..18d9810 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings new file mode 100755 index 0000000..0c67376 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/project.xcworkspace/xcshareddata/WorkspaceSettings.xcsettings @@ -0,0 +1,5 @@ + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/xcshareddata/xcschemes/GJAssignment.xcscheme b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/xcshareddata/xcschemes/GJAssignment.xcscheme new file mode 100755 index 0000000..82a9100 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcodeproj/xcshareddata/xcschemes/GJAssignment.xcscheme @@ -0,0 +1,122 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/contents.xcworkspacedata b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/contents.xcworkspacedata new file mode 100755 index 0000000..388b541 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,10 @@ + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100755 index 0000000..18d9810 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegate.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegate.swift new file mode 100755 index 0000000..aa169fa --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegate.swift @@ -0,0 +1,31 @@ +// +// AppDelegate.swift +// GJAssignment +// +// Created by Anonymous on 14/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit +import CoreData + +@UIApplicationMain +class AppDelegate: UIResponder, UIApplicationDelegate { + + var window: UIWindow? + + func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { + + setupNavigationBarAppearance() + + window = UIWindow(frame: UIScreen.main.bounds) + window?.rootViewController = getRootViewController() + window?.makeKeyAndVisible() + + return true + } + + func applicationWillTerminate(_ application: UIApplication) { + CoreDataManager.shared.saveContext() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegateExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegateExtension.swift new file mode 100755 index 0000000..962042c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/AppDelegate/AppDelegateExtension.swift @@ -0,0 +1,22 @@ +// +// AppDelegateExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +extension AppDelegate { + func setupNavigationBarAppearance() { + UINavigationBar.appearance().tintColor = UIColor.Common.tint + } + + func getRootViewController() -> UIViewController { + let contactViewController = ContactListViewController.get() + let rootNavigationController = UINavigationController(rootViewController: contactViewController) + return rootNavigationController + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Base.lproj/LaunchScreen.storyboard b/Go-Jek/GO-JEK-Assignment/GJAssignment/Base.lproj/LaunchScreen.storyboard new file mode 100755 index 0000000..87d40dd --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Base.lproj/LaunchScreen.storyboard @@ -0,0 +1,52 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.swift new file mode 100755 index 0000000..f25a511 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.swift @@ -0,0 +1,126 @@ +// +// AddContactViewController.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +protocol AddContactViewControllerDelegate: class { + func contactSyncSuccessfully(contact: Contact) +} + +class AddContactViewController: UIViewController { + + // MARK: - Outlet + @IBOutlet weak var contactImageView: CircularImageView! + @IBOutlet weak var selectImageButton: UIButton! + @IBOutlet weak var contactTableView: UITableView! + + // MARK: - Weak Properties and Delgate + weak var tableViewHeader: ContactEditTableViewHeader! + weak var delegate: AddContactViewControllerDelegate? + + // MARK: - Internal Properties + var imagePicker: ImagePicker! + + // MARK: - Properties + var viewModel: AddContactViewModel! + + // MARK: - Class Functions + class func present(contact: Contact?, delegate: AddContactViewControllerDelegate? = nil) { + let addContactViewController = AddContactViewController(nibName: AddContactViewController.name, bundle: nil) + addContactViewController.delegate = delegate + addContactViewController.viewModel = AddContactViewModel(contact: contact) + let navigationController = UINavigationController(rootViewController: addContactViewController) + UIApplication.shared.keyWindow?.rootViewController?.present( + navigationController, animated: true, completion: nil) + } + + // MARK: - View Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + + setupTableView() + setupImagePicker() + setupBindingAndGetContact() + setupNavigationBarButtonItems() + } + + override func viewDidLayoutSubviews() { + super.viewDidLayoutSubviews() + let frame = CGRect(x: 0, y: 0, width: view.frame.width, height: 150) + contactTableView.tableHeaderView?.frame = frame + } + + // MARK: - Helper Functions + private func setupNavigationBarButtonItems() { + navigationController?.navigationBar.shadowImage = UIImage() + + //set cancel and done bar button item + let cancelBarButtonItem = UIBarButtonItem(title: viewModel?.cancelBarButtonTitle, + style: .plain, + target: self, + action: #selector(cancelBarButtonItemAction)) + navigationItem.leftBarButtonItem = cancelBarButtonItem + + let doneBarButtonItem = UIBarButtonItem(title: viewModel?.doneBarButtonTitle, + style: .done, + target: self, + action: #selector(doneBarButtonItemAction)) + navigationItem.rightBarButtonItem = doneBarButtonItem + } + + private func setupTableView() { + contactTableView.delegate = self + contactTableView.dataSource = self + contactTableView.tableFooterView = UIView(frame: CGRect.zero) + contactTableView.register(ContactEditTableViewCell.nib, forCellReuseIdentifier: ContactEditTableViewCell.identifier) + contactTableView.accessibilityIdentifier = "addContactTableView" + + tableViewHeader = ContactEditTableViewHeader.get() + tableViewHeader.delegate = self + contactTableView.tableHeaderView = tableViewHeader + } + + private func setupImagePicker() { + imagePicker = ImagePicker(from: self) + imagePicker.delegate = self + } + + private func setupBindingAndGetContact() { + //Binding + viewModel.isBusy.bind { [unowned self] isBusy in + self.navigationController?.view.showLoader(show: isBusy) + } + + viewModel.isContactSync.bind { [unowned self] (isSync) in + if isSync { + self.delegate?.contactSyncSuccessfully(contact: self.viewModel.contact.value) + self.dismiss(animated: true, completion: nil) + } + } + + viewModel.contact.bind(listener: {[unowned self] (_) in + self.contactTableView.reloadData() + }) + + viewModel.error.bind { [unowned self] (error) in + if let error = error { + UIAlertController.show(error.localizedDescription, from: self) + } + } + } + + @objc private func cancelBarButtonItemAction() { + view.endEditing(true) + dismiss(animated: true, completion: nil) + } + + @objc private func doneBarButtonItemAction() { + view.endEditing(true) + viewModel.syncContact() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.xib new file mode 100755 index 0000000..0213175 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewController.xib @@ -0,0 +1,49 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewControllerExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewControllerExtension.swift new file mode 100755 index 0000000..a57d8ac --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/AddContactViewController/AddContactViewControllerExtension.swift @@ -0,0 +1,62 @@ +// +// AddContactViewControllerExtension.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +import UIKit + +extension AddContactViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel.contactMetadata?.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactEditTableViewCell.identifier, + for: indexPath) as? ContactEditTableViewCell else { + fatalError("Unable to dequeue ContactDetailsTableViewCell cell.") + } + cell.delegate = self + cell.accessibilityIdentifier = String(format: "editTableViewCell_%ld", indexPath.row) + cell.config(metaData: viewModel!.contactMetadata[indexPath.row]) + return cell + } +} + +extension AddContactViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 56 + } +} + +extension AddContactViewController: ContactEditTableViewHeaderDelegate { + func takeImageButtonAction(_ sender: UIButton) { + imagePicker.showImagePickerSources(sender: sender) + } +} + +extension AddContactViewController: ImagePickerDelegate { + func didFinishPickingImage(_ image: UIImage?) { + tableViewHeader.imageView.image = image + } +} + +extension AddContactViewController: ContactEditTableViewCellDelegate { + func textChanged(contactMetaData: ContactMetadata, text: String) { + contactMetaData.info = text + switch contactMetaData.type { + case .firstName: + viewModel.contact.value.firstName = text + case .lastName: + viewModel.contact.value.lastName = text + case .email: + viewModel.contact.value.email = text + case .mobile: + viewModel.contact.value.phoneNumber = text + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.swift new file mode 100755 index 0000000..b67f103 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.swift @@ -0,0 +1,132 @@ +// +// ContactDetailsViewController.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +class ContactDetailsViewController: UIViewController { + + // MARK: - Outlet + @IBOutlet weak var userImageView: UIImageView! + @IBOutlet weak var userName: UILabel! + @IBOutlet weak var topContainerView: UIView! + @IBOutlet weak var detailsTableView: UITableView! + @IBOutlet weak var favouriteImageView: UIImageView! + + // MARK: - Private Properties + private var navigationBarShadowImage: UIImage? + + // MARK: - Internal Properties + var viewModel: ContactDetailsViewModel! + + // MARK: - Class Functions + class func get(contact: Contact) -> ContactDetailsViewController { + let detailsViewController = ContactDetailsViewController(nibName: ContactDetailsViewController.name, bundle: nil) + detailsViewController.viewModel = ContactDetailsViewModel(contact: contact) + return detailsViewController + } + + // MARK: - View Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + + setupTableView() + setupBindingAndGetContact() + setupNavigationBarButtonItems() + } + + override func viewWillAppear(_ animated: Bool) { + super.viewWillAppear(animated) + navigationController?.navigationBar.shadowImage = UIImage() + } + + override func viewWillDisappear(_ animated: Bool) { + super.viewWillDisappear(animated) + navigationController?.navigationBar.shadowImage = navigationBarShadowImage + } + + // MARK: - Helper Functions + private func setupNavigationBarButtonItems() { + //Store original shadow image + navigationBarShadowImage = navigationController?.navigationBar.shadowImage + + //set edit contact bar button item + let editBarButtonItem = UIBarButtonItem(title: viewModel?.editBarButtonTitle, + style: .plain, + target: self, + action: #selector(editBarButtonItemAction)) + navigationItem.rightBarButtonItem = editBarButtonItem + } + + private func setupTableView() { + detailsTableView.delegate = self + detailsTableView.dataSource = self + detailsTableView.tableFooterView = UIView(frame: CGRect.zero) + detailsTableView.register(ContactDetailsTableViewCell.nib, forCellReuseIdentifier: ContactDetailsTableViewCell.identifier) + detailsTableView.accessibilityIdentifier = "detailsTableView" + } + + private func setupBindingAndGetContact() { + //Binding + viewModel.isBusy.bind { [unowned self] isBusy in + self.navigationController?.view.showLoader(show: isBusy) + } + + viewModel.contact.bind(listener: {[unowned self] (_) in + let isFavorite = self.viewModel?.isFavorite ?? false + let image = isFavorite ? UIImage.Action.favoriteSelected : UIImage.Action.favorite + self.favouriteImageView.image = image + + self.userImageView.image = UIImage.Contact.placeHolder + self.userName.text = self.viewModel?.name + self.detailsTableView.reloadData() + }) + + viewModel.error.bind { [unowned self] (error) in + if let error = error { + UIAlertController.show(error.localizedDescription, from: self) + } + } + + viewModel.getContactDetails() + } + + // MARK: - Actions + @objc private func editBarButtonItemAction() { + AddContactViewController.present(contact: viewModel.contact.value, delegate: self) + } + + @IBAction func messageTapGestureAction(_ sender: UITapGestureRecognizer) { + guard let messageURL = viewModel.messageURL else { + UIAlertController.show("Contact number not valid.", from: self) + return + } + UIApplication.shared.open(messageURL, options: [:], completionHandler: nil) + } + + @IBAction func callTapGestureAction(_ sender: UITapGestureRecognizer) { + guard let telURL = viewModel.telURL else { + UIAlertController.show("Contact number not valid.", from: self) + return + } + UIApplication.shared.open(telURL, options: [:], completionHandler: nil) + } + + @IBAction func emailTapGestureAction(_ sender: UITapGestureRecognizer) { + guard let mailURL = viewModel.mailURL else { + UIAlertController.show("Contact email not valid.", from: self) + return + } + UIApplication.shared.open(mailURL, options: [:], completionHandler: nil) + } + + @IBAction func favouriteTapGestureAction(_ sender: UITapGestureRecognizer) { + if sender.state == .recognized { + viewModel.updateFavourite() + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.xib new file mode 100755 index 0000000..0496bd5 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewController.xib @@ -0,0 +1,221 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewControllerExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewControllerExtension.swift new file mode 100755 index 0000000..3fc92b4 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactDetailsViewController/ContactDetailsViewControllerExtension.swift @@ -0,0 +1,38 @@ +// +// ContactDetailsViewControllerExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +extension ContactDetailsViewController: UITableViewDataSource { + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return viewModel.contactMetadata.count + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactDetailsTableViewCell.identifier, + for: indexPath) as? ContactDetailsTableViewCell else { + fatalError("Unable to dequeue ContactDetailsTableViewCell cell.") + } + cell.config(metaData: viewModel.contactMetadata[indexPath.row]) + cell.accessibilityIdentifier = String(format: "detailsTableViewCell_%ld", indexPath.row) + return cell + } +} + +extension ContactDetailsViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, heightForRowAt indexPath: IndexPath) -> CGFloat { + return 56 + } +} + +extension ContactDetailsViewController: AddContactViewControllerDelegate { + func contactSyncSuccessfully(contact: Contact) { + viewModel.contact.value = contact + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.swift new file mode 100755 index 0000000..65db29c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.swift @@ -0,0 +1,107 @@ +// +// ContactViewController.swift +// GJAssignment +// +// Created by Anonymous on 16/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit +import CoreData + +class ContactListViewController: UIViewController { + + // MARK: - Outlet + @IBOutlet weak var contactTableView: UITableView! + + // MARK: - Private Properties + private let viewModel = ContactListViewModel() + var fetchedResultController: NSFetchedResultsController? + + // MARK: - Class Functions + class func get() -> ContactListViewController { + let contactViewController = ContactListViewController(nibName: ContactListViewController.name, bundle: nil) + return contactViewController + } + + // MARK: - View Lifecycle + override func viewDidLoad() { + super.viewDidLoad() + + title = viewModel.title + + contactTableView.delegate = self + contactTableView.dataSource = self + contactTableView.tableFooterView = UIView(frame: CGRect.zero) + contactTableView.register(ContactTableViewCell.nib, forCellReuseIdentifier: ContactTableViewCell.identifier) + contactTableView.accessibilityIdentifier = "contactListTableView" + + setupNavigationBarButtonItems() + setupFetchedResultController() + setupBindingAndGetContacts() + } + + // MARK: - Helper Functions + private func setupNavigationBarButtonItems() { + let groupsBarButtonItem = UIBarButtonItem(title: viewModel.groupBarButtonTitle, + style: .plain, + target: self, + action: nil) + navigationItem.leftBarButtonItem = groupsBarButtonItem + + let addContactBarButtonItem = UIBarButtonItem(barButtonSystemItem: .add, + target: self, + action: #selector(addContactBarButtonItemAction)) + navigationItem.rightBarButtonItem = addContactBarButtonItem + } + + private func setupFetchedResultController() { + let contactsFetchRequest: NSFetchRequest = Contact.fetchRequest() + let sortDescriptor = NSSortDescriptor(key: #keyPath(Contact.firstName), + ascending: true, + selector: #selector(NSString.caseInsensitiveCompare(_:))) + contactsFetchRequest.sortDescriptors = [sortDescriptor] + let managedObjectContext = CoreDataManager.shared.managedObjectContext + fetchedResultController = .init(fetchRequest: contactsFetchRequest, + managedObjectContext: managedObjectContext, + sectionNameKeyPath: #keyPath(Contact.sectionTitle), + cacheName: nil) + fetchedResultController?.delegate = self + } + + private func performFetchRequest() { + do { + try fetchedResultController?.performFetch() + contactTableView.reloadData() + } catch { + Log.error("Unable to perform fetch operation from DB.", error: error) + } + } + + private func setupBindingAndGetContacts() { + //Binding + viewModel.isBusy.bind { [unowned self] isBusy in + self.view.showLoader(show: isBusy) + } + + viewModel.contacts.bind { [unowned self] (contacts) in + if contacts != nil { + self.performFetchRequest() + } + } + + viewModel.error.bind { [unowned self] (error) in + if let error = error { + self.performFetchRequest() + UIAlertController.show(error.localizedDescription, from: self) + } + } + + //Get Contacts + viewModel.getContacts() + } + + @objc private func addContactBarButtonItemAction() { + AddContactViewController.present(contact: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.xib new file mode 100755 index 0000000..9dd7bd6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewController.xib @@ -0,0 +1,50 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewControllerExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewControllerExtension.swift new file mode 100755 index 0000000..9edabce --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Controllers/ContactListViewController/ContactListViewControllerExtension.swift @@ -0,0 +1,88 @@ +// +// ContactListViewControllerExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit +import CoreData + +// MARK: - NSFetchResultController Delegate +extension ContactListViewController: NSFetchedResultsControllerDelegate { + func controllerWillChangeContent(_ controller: NSFetchedResultsController) { + contactTableView.beginUpdates() + } + + func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { + switch type { + case .insert: + contactTableView.insertRows(at: [newIndexPath!], with: .none) + case .delete: + contactTableView.deleteRows(at: [indexPath!], with: .none) + case .update: + contactTableView.reloadRows(at: [indexPath!], with: .none) + case .move: + contactTableView.moveRow(at: indexPath!, to: newIndexPath!) + default: + break + } + } + + func controller(_ controller: NSFetchedResultsController, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { + switch type { + case .insert: + contactTableView.insertSections(IndexSet(integer: sectionIndex), with: .none) + case .delete: + contactTableView.deleteSections(IndexSet(integer: sectionIndex), with: .none) + case .update: + contactTableView.reloadSections(IndexSet(integer: sectionIndex), with: .none) + default: + break + } + } + + func controllerDidChangeContent(_ controller: NSFetchedResultsController) { + contactTableView.endUpdates() + } + +} + +// MARK: - UITableView Data Source +extension ContactListViewController: UITableViewDataSource { + func numberOfSections(in tableView: UITableView) -> Int { + return fetchedResultController?.sections?.count ?? 0 + } + + func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return fetchedResultController?.sections?[section].objects?.count ?? 0 + } + + func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + guard let cell = tableView.dequeueReusableCell(withIdentifier: ContactTableViewCell.identifier, + for: indexPath) as? ContactTableViewCell else { + fatalError("Unable to dequeue ContactTableViewCell.") + } + cell.config(contact: fetchedResultController!.object(at: indexPath)) + cell.accessibilityIdentifier = String(format: "contactTableViewCell_%ld_%ld", indexPath.section, indexPath.row) + return cell + } + + func sectionIndexTitles(for tableView: UITableView) -> [String]? { + return fetchedResultController?.sectionIndexTitles + } + + func tableView(_ tableView: UITableView, titleForHeaderInSection section: Int) -> String? { + return fetchedResultController?.sections?[section].indexTitle + } +} + +extension ContactListViewController: UITableViewDelegate { + func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let contact = fetchedResultController!.object(at: indexPath) + let contactDetailsViewController = ContactDetailsViewController.get(contact: contact) + navigationController?.pushViewController(contactDetailsViewController, animated: true) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/.xccurrentversion b/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/.xccurrentversion new file mode 100755 index 0000000..5eb505b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + GJAssignment.xcdatamodel + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/GJAssignment.xcdatamodel/contents b/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/GJAssignment.xcdatamodel/contents new file mode 100755 index 0000000..0902e29 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/GJAssignment.xcdatamodeld/GJAssignment.xcdatamodel/contents @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Info.plist b/Go-Jek/GO-JEK-Assignment/GJAssignment/Info.plist new file mode 100755 index 0000000..8d74583 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Info.plist @@ -0,0 +1,45 @@ + + + + + NSPhotoLibraryUsageDescription + App requires access to your phone’s photo lib. + NSCameraUsageDescription + App requires access to your phone’s camera. + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + APPL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + LSRequiresIPhoneOS + + UILaunchStoryboardName + LaunchScreen + UIRequiredDeviceCapabilities + + armv7 + + UISupportedInterfaceOrientations + + UIInterfaceOrientationPortrait + + UISupportedInterfaceOrientations~ipad + + UIInterfaceOrientationPortrait + UIInterfaceOrientationPortraitUpsideDown + UIInterfaceOrientationLandscapeLeft + UIInterfaceOrientationLandscapeRight + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataClass.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataClass.swift new file mode 100755 index 0000000..08c5e33 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataClass.swift @@ -0,0 +1,79 @@ +// +// Contact+CoreDataClass.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// +// + +import Foundation +import CoreData + +extension Contact { + + public var fullName: String { + let seprator = (firstName != nil) ? " " : "" + return (firstName ?? "") + seprator + (lastName ?? "") + } + + @objc public var sectionTitle: String { + let firstCharString = firstName?.first?.uppercased() ?? "" + if firstCharString >= "A" && firstCharString <= "Z" { + return firstCharString + } + return "#" + } + + @nonobjc public class func fetchRequest() -> NSFetchRequest { + return NSFetchRequest(entityName: Contact.name) + } + + public class func getContact(id: Int) -> Contact? { + let fetchRequest: NSFetchRequest = Contact.fetchRequest() + fetchRequest.predicate = NSPredicate(format: "id=%ld", id) + fetchRequest.fetchLimit = 1 + do { + let contacts: [Contact] = try CoreDataManager.shared.managedObjectContext.fetch(fetchRequest) + return contacts.first + } catch { + Log.error("Unable to fetch contact with id \(id).", error: error) + } + return nil + } + + public class func deleteAllContacts() { + let fetchRequest: NSFetchRequest = Contact.fetchRequest() + let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + deleteRequest.resultType = .resultTypeObjectIDs + + // perform the delete + do { + let managedObjectContext = CoreDataManager.shared.managedObjectContext + let result = try managedObjectContext.execute(deleteRequest) as? NSBatchDeleteResult + let managedObjectIds = result?.result as? [NSManagedObjectID] ?? [] + let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: managedObjectIds] + NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [managedObjectContext]) + } catch let error as NSError { + Log.error("Unable to delete existing contacts.", error: error) + } + } + + func getDetailsMetadata() -> [ContactMetadata] { + let phoneMetadata = ContactMetadata(desc: NSLocalizedString("mobile", comment: ""), + info: phoneNumber, type: .mobile, keyboardType: .phonePad) + let emailMetadata = ContactMetadata(desc: NSLocalizedString("email", comment: ""), + info: email, type: .email, keyboardType: .emailAddress) + return [phoneMetadata, emailMetadata] + } + + func getEditMetaData() -> [ContactMetadata] { + let firstNameMetaData = ContactMetadata(desc: NSLocalizedString("First Name", comment: ""), + info: firstName, type: .firstName) + + let lastNameMetaData = ContactMetadata(desc: NSLocalizedString("Last Name", comment: ""), + info: lastName, type: .lastName) + + return [firstNameMetaData, lastNameMetaData] + getDetailsMetadata() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataProperties.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataProperties.swift new file mode 100755 index 0000000..b091208 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/Contact+CoreDataProperties.swift @@ -0,0 +1,75 @@ +// +// Contact+CoreDataProperties.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// +// + +import Foundation +import CoreData + +@objc(Contact) +public class Contact: NSManagedObject, Codable { + @NSManaged public var id: Int64 + @NSManaged public var lastName: String? + @NSManaged public var firstName: String? + @NSManaged public var profilePic: String? + @NSManaged public var favorite: Bool + @NSManaged public var email: String? + @NSManaged public var phoneNumber: String? + + enum CodingKeys: String, CodingKey { + case id, favorite, email + case firstName = "first_name" + case lastName = "last_name" + case profilePic = "profile_pic" + case phoneNumber = "phone_number" + } + + public required convenience init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + let contactId = try container.decodeIfPresent(Int.self, forKey: .id) ?? 0 + let firstName = try container.decodeIfPresent(String.self, forKey: .firstName) + let lastName = try container.decodeIfPresent(String.self, forKey: .lastName) + let profilePic = try container.decodeIfPresent(String.self, forKey: .profilePic) + let favorite = try container.decodeIfPresent(Bool.self, forKey: .favorite) ?? false + let phoneNumber = try container.decodeIfPresent(String.self, forKey: .phoneNumber) + let email = try container.decodeIfPresent(String.self, forKey: .email) + + let managedObjectContext = CoreDataManager.shared.managedObjectContext + guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { + fatalError("Failed to decode Contact") + } + self.init(entity: entity, insertInto: nil) + self.id = Int64(contactId) + self.firstName = firstName + self.lastName = lastName + self.profilePic = profilePic + self.favorite = favorite + self.phoneNumber = phoneNumber + self.email = email + + if let entity = Contact.getContact(id: contactId) { + entity.firstName = firstName + entity.lastName = lastName + entity.profilePic = profilePic + entity.favorite = favorite + entity.phoneNumber = phoneNumber + entity.email = email + } else { + managedObjectContext.insert(self) + } + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(firstName, forKey: .firstName) + try container.encode(lastName, forKey: .lastName) + //try container.encode(profilePic, forKey: .profilePic) + try container.encode(favorite, forKey: .favorite) + try container.encode(phoneNumber, forKey: .phoneNumber) + try container.encode(email, forKey: .email) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ContactMetaData.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ContactMetaData.swift new file mode 100755 index 0000000..175af89 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ContactMetaData.swift @@ -0,0 +1,28 @@ +// +// ContactMetaData.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +enum ContactMetadataType { + case firstName, lastName, email, mobile +} + +class ContactMetadata { + var desc: String! + var info: String! + var type: ContactMetadataType + var keyboardType: UIKeyboardType + + init(desc: String, info: String?, type: ContactMetadataType, keyboardType: UIKeyboardType = .default) { + self.desc = desc + self.info = info + self.type = type + self.keyboardType = keyboardType + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ServerError.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ServerError.swift new file mode 100755 index 0000000..2765b12 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Models/ServerError.swift @@ -0,0 +1,14 @@ +// +// ServerError.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +struct ServerError: Decodable { + let status: String? + let error: String? +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/AppServices/ContactService.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/AppServices/ContactService.swift new file mode 100755 index 0000000..325670b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/AppServices/ContactService.swift @@ -0,0 +1,72 @@ +// +// ContactService.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +//Contact APIs +enum ContactAPI { + case getContacts + case getContact(id: Int) + case deleteContact(id: Int) + case addContact(contact: Contact) + case updateContact(id: Int, contact: Contact) +} + +extension ContactAPI: RequestProtocol { + //Set Base URL + var baseURL: URL { + guard let url = URL(string: Constants.Service.baseURL) else { + fatalError("BaseURL could not be configured.") + } + return url + } + + //Returns EndPoint for Contact APIs + var path: String { + switch self { + case .getContacts, .addContact: + return "contacts.json" + case .getContact(let id), .deleteContact(let id), .updateContact(let id, _): + return "contacts/\(String(describing: id)).json" + } + } + + //Returns HTTP Method for contact APIs + var httpMethod: HTTPMethod { + switch self { + case .getContacts, .getContact: + return .get + case .addContact: + return .post + case .updateContact: + return .put + case .deleteContact: + return .delete + } + } + + //Encode and Returns Encoded Data + var httpBody: Data? { + switch self { + case .addContact(let contact), .updateContact(_, let contact): + do { + return try JSONEncoder().encode(contact) + } catch { + Log.error("Unable to encode Contact.", error: error) + } + default: + return nil + } + return nil + } + + //Return Contact APIs Specific Headers + var headers: HTTPHeaders? { + return nil + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Constants/NetworkConstants.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Constants/NetworkConstants.swift new file mode 100755 index 0000000..5df142f --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Constants/NetworkConstants.swift @@ -0,0 +1,16 @@ +// +// Constants.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +extension Constants { + struct Service { + static let baseURL = "https://gojek-contacts-app.herokuapp.com/" + static let timeout: TimeInterval = 60.0 + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLResponseExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLResponseExtension.swift new file mode 100755 index 0000000..eb40b1c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLResponseExtension.swift @@ -0,0 +1,22 @@ +// +// URLResponseExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +extension URLResponse { + var isSuccess: Bool { + return httpStatusCode >= 200 && httpStatusCode < 300 + } + + var httpStatusCode: Int { + guard let statusCode = (self as? HTTPURLResponse)?.statusCode else { + return 0 + } + return statusCode + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionDataTaskExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionDataTaskExtension.swift new file mode 100755 index 0000000..d7abed7 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionDataTaskExtension.swift @@ -0,0 +1,13 @@ +// +// URLSessionDataTaskExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +extension URLSessionDataTask: URLSessionDataTaskProtocol { + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionExtension.swift new file mode 100755 index 0000000..4330838 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Extensions/URLSessionExtension.swift @@ -0,0 +1,16 @@ +// +// URLSessionExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +extension URLSession: URLSessionProtocol { + func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { + let task = dataTask(with: request, completionHandler: completionHandler) as URLSessionDataTask + return task as URLSessionDataTaskProtocol + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/GJError.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/GJError.swift new file mode 100755 index 0000000..276f9d2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/GJError.swift @@ -0,0 +1,18 @@ +// +// GJError.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +typealias GJErrorHandler = (GJError?) -> Void + +struct GJError: Error { + var localizedDescription: String + init(_ localizedDescription: String) { + self.localizedDescription = localizedDescription + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPClient.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPClient.swift new file mode 100755 index 0000000..8fd1b1c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPClient.swift @@ -0,0 +1,86 @@ +// +// NetworkManager.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +class HTTPClient { + // MARK: Typealias + typealias CompletionResult = (Result) -> Void + + // MARK: - Shared Instance + static let shared = HTTPClient(session: URLSession.shared) + + // MARK: - Private Properties + private let session: URLSessionProtocol + private var task: URLSessionDataTaskProtocol? + private var completionResult: CompletionResult? + + // MARK: - Initialiser + init(session: URLSessionProtocol) { + self.session = session + } + + // MARK: - Data Task Helper + func dataTask(_ request: RequestProtocol, completion: @escaping CompletionResult) { + completionResult = completion + var urlRequest = URLRequest(url: request.baseURL.appendingPathComponent(request.path), + cachePolicy: .reloadIgnoringLocalAndRemoteCacheData, + timeoutInterval: Constants.Service.timeout) + urlRequest.httpMethod = request.httpMethod.rawValue + urlRequest.httpBody = request.httpBody + urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type") + + task = session.dataTask(with: urlRequest) { (data, response, error) in + //return error if there is any error in making request + if let error = error { + self.completionResult(.failure(GJError(error.localizedDescription))) + return + } + + //check response + if let response = response, response.isSuccess { + if let data = data { + self.completionResult(.success(data)) + } + + if response.httpStatusCode == 204 { + self.completionResult(.success(nil)) + } + } else { + let commonErrorMessage = NSLocalizedString("Somthing went wrong!", comment: "") + guard let data = data else { + Log.error(commonErrorMessage) + self.completionResult(.failure(GJError(commonErrorMessage))) + return + } + do { + let serverError = try JSONDecoder().decode(ServerError.self, from: data) + Log.error(serverError.error ?? commonErrorMessage) + self.completionResult(.failure(GJError(serverError.error ?? commonErrorMessage))) + } catch { + Log.error(commonErrorMessage, error: error) + self.completionResult(.failure(GJError(commonErrorMessage))) + } + } + } + + //Resume task + self.task?.resume() + } + + func cancel() { + self.task?.cancel() + } + + // MARK: - Private Helper Function + private func completionResult(_ result: Result) { + DispatchQueue.main.async { + self.completionResult?(result) + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPMethod.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPMethod.swift new file mode 100755 index 0000000..4dde073 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/HTTPClient/HTTPMethod.swift @@ -0,0 +1,17 @@ +// +// HTTPMethod.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +enum HTTPMethod: String { + case get = "GET" + case post = "POST" + case put = "PUT" + case patch = "PATCH" + case delete = "DELETE" +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/RequestProtocol.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/RequestProtocol.swift new file mode 100755 index 0000000..939e4b7 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/RequestProtocol.swift @@ -0,0 +1,19 @@ +// +// RequestProtocol.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +public typealias HTTPHeaders = [String: String] + +protocol RequestProtocol { + var baseURL: URL { get } + var path: String { get } + var httpMethod: HTTPMethod { get } + var httpBody: Data? { get } + var headers: HTTPHeaders? { get } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionDataTaskProtocol.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionDataTaskProtocol.swift new file mode 100755 index 0000000..585e05c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionDataTaskProtocol.swift @@ -0,0 +1,14 @@ +// +// URLSessionTaskProtocol.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +protocol URLSessionDataTaskProtocol { + func resume() + func cancel() +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionProtocol.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionProtocol.swift new file mode 100755 index 0000000..97b66bd --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Networking/Protocols/URLSessionProtocol.swift @@ -0,0 +1,13 @@ +// +// URLSessionProtocol.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +protocol URLSessionProtocol { + func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100755 index 0000000..d8db8d6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "20x20", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "29x29", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "40x40", + "scale" : "3x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "2x" + }, + { + "idiom" : "iphone", + "size" : "60x60", + "scale" : "3x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "20x20", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "29x29", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "40x40", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "1x" + }, + { + "idiom" : "ipad", + "size" : "76x76", + "scale" : "2x" + }, + { + "idiom" : "ipad", + "size" : "83.5x83.5", + "scale" : "2x" + }, + { + "idiom" : "ios-marketing", + "size" : "1024x1024", + "scale" : "1x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/Contents.json new file mode 100755 index 0000000..658302d --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "call_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "call_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@2x.png new file mode 100755 index 0000000..e61457c Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@3x.png new file mode 100755 index 0000000..9bdcd7c Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CallButton.imageset/call_button@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/Contents.json new file mode 100755 index 0000000..607bb6e --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "camera_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "camera_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@2x.png new file mode 100755 index 0000000..abea2bb Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@3x.png new file mode 100755 index 0000000..a96aa29 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/CameraButton.imageset/camera_button@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/Contents.json new file mode 100755 index 0000000..da4a164 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/Contents.json new file mode 100755 index 0000000..8c833cd --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "email_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "email_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@2x.png new file mode 100755 index 0000000..2419b18 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@3x.png new file mode 100755 index 0000000..50d1177 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/EmailButton.imageset/email_button@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/Contents.json new file mode 100755 index 0000000..93346d2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "favourite_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "favourite_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@2x.png new file mode 100755 index 0000000..bbcbb7e Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@3x.png new file mode 100755 index 0000000..70daf5d Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButton.imageset/favourite_button@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/Contents.json new file mode 100755 index 0000000..057ed75 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "favourite_button_selected@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "favourite_button_selected@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@2x.png new file mode 100755 index 0000000..58042b3 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@3x.png new file mode 100755 index 0000000..2c68e0a Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/FavouriteButtonSelected.imageset/favourite_button_selected@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/Contents.json new file mode 100755 index 0000000..6653fc0 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "home_favourite@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "home_favourite@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@2x.png new file mode 100755 index 0000000..6022521 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@3x.png new file mode 100755 index 0000000..775a96c Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/HomeFavourite.imageset/home_favourite@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/Contents.json new file mode 100755 index 0000000..802bd6b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "message_button@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "message_button@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@2x.png new file mode 100755 index 0000000..75ea4c3 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@3x.png new file mode 100755 index 0000000..bf9bca2 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/MessageButton.imageset/message_button@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/Contents.json new file mode 100755 index 0000000..fcc8db0 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/Contents.json @@ -0,0 +1,22 @@ +{ + "images" : [ + { + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "filename" : "placeholder_photo@2x.png", + "scale" : "2x" + }, + { + "idiom" : "universal", + "filename" : "placeholder_photo@3x.png", + "scale" : "3x" + } + ], + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@2x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@2x.png new file mode 100755 index 0000000..54e2cce Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@2x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@3x.png b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@3x.png new file mode 100755 index 0000000..cbc4ada Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Assets.xcassets/PlaceholderPhoto.imageset/placeholder_photo@3x.png differ diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Contents.json new file mode 100755 index 0000000..da4a164 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/DescText.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/DescText.colorset/Contents.json new file mode 100755 index 0000000..89302f3 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/DescText.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0x4A", + "alpha" : "0.500", + "blue" : "0x4A", + "green" : "0x4A" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Primary.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Primary.colorset/Contents.json new file mode 100755 index 0000000..0081319 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/Primary.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0.314", + "alpha" : "1.000", + "blue" : "0.761", + "green" : "0.890" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/PrimaryText.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/PrimaryText.colorset/Contents.json new file mode 100755 index 0000000..2662ec4 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/PrimaryText.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0x4A", + "alpha" : "1.000", + "blue" : "0x4A", + "green" : "0x4A" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSectionBackground.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSectionBackground.colorset/Contents.json new file mode 100755 index 0000000..3a499ec --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSectionBackground.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0xE8", + "alpha" : "1.000", + "blue" : "0xE8", + "green" : "0xE8" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSeparator.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSeparator.colorset/Contents.json new file mode 100755 index 0000000..623967d --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/TableSeparator.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0xF0", + "alpha" : "1.000", + "blue" : "0xF0", + "green" : "0xF0" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/ViewBackground.colorset/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/ViewBackground.colorset/Contents.json new file mode 100755 index 0000000..d1021e2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Common/ViewBackground.colorset/Contents.json @@ -0,0 +1,20 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + }, + "colors" : [ + { + "idiom" : "universal", + "color" : { + "color-space" : "srgb", + "components" : { + "red" : "0xF9", + "alpha" : "1.000", + "blue" : "0xF9", + "green" : "0xF9" + } + } + } + ] +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Contents.json b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Contents.json new file mode 100755 index 0000000..da4a164 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Colors.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "version" : 1, + "author" : "xcode" + } +} \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Localizable.strings b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Localizable.strings new file mode 100755 index 0000000..c3dcd14 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Resources/Localizable.strings @@ -0,0 +1,20 @@ +/* + Localizable.strings + GJAssignment + + Created by Anonymous on 17/08/19. + Copyright © 2019 Anonymous. All rights reserved. +*/ + +"Contact" = "Contact"; +"Groups" = "Groups"; +"Edit" = "Edit"; +"Cancel" = "Cancel"; +"Done" = "Done"; +"message" = "message"; +"call" = "call"; +"email" = "email"; +"favourite" = "favourite"; +"First Name" = "First Name"; +"Last Name" = "Last Name"; +"mobile" = "mobile"; diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/Bindable.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/Bindable.swift new file mode 100755 index 0000000..e5634ba --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/Bindable.swift @@ -0,0 +1,34 @@ +// +// Bindable.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +class Bindable { + typealias Listener = (T) -> Void + var listener: Listener? + + var value: T { + didSet { + DispatchQueue.main.async { [weak self] in + guard let self = self else { + return + } + self.listener?(self.value) + } + } + } + + init(_ value: T) { + self.value = value + } + + func bind(listener: Listener?) { + self.listener = listener + listener?(value) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/BindableTextField.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/BindableTextField.swift new file mode 100755 index 0000000..c3dc7b1 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Bindable/BindableTextField.swift @@ -0,0 +1,24 @@ +// +// BindableTextField.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +class BindableTextField: UITextField { + typealias Listener = (String) -> Void + var textChanged: Listener = { _ in } + + func bind(listener: @escaping Listener) { + self.textChanged = listener + self.addTarget(self, action: #selector(textFieldDidChanged(_:)), for: .editingChanged) + } + + @objc func textFieldDidChanged(_ textField: UITextField) { + self.textChanged(textField.text!) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Constants.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Constants.swift new file mode 100755 index 0000000..c402233 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Constants.swift @@ -0,0 +1,13 @@ +// +// Constants.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +struct Constants { + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CoreDataManager/CoreDataManager.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CoreDataManager/CoreDataManager.swift new file mode 100755 index 0000000..ad023c6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CoreDataManager/CoreDataManager.swift @@ -0,0 +1,55 @@ +// +// CoreDataManager.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import CoreData + +class CoreDataManager { + static let shared = CoreDataManager() + + private init() { + } + + // MARK: - Core Data stack + lazy var persistentContainer: NSPersistentContainer = { + /* + The persistent container for the application. This implementation + creates and returns a container, having loaded the store for the + application to it. This property is optional since there are legitimate + error conditions that could cause the creation of the store to fail. + */ + let container = NSPersistentContainer(name: "GJAssignment") + container.loadPersistentStores(completionHandler: { (storeDescription, error) in + if let error = error as NSError? { + Log.error("Unresolved error \(error), \(error.userInfo)") + } else { + Log.debug("Store Description \(storeDescription)") + } + }) + return container + }() + + var managedObjectContext: NSManagedObjectContext { + return persistentContainer.viewContext + } + + // MARK: - Core Data Saving support + func saveContext () { + let context = persistentContainer.viewContext + if context.hasChanges { + do { + try context.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nserror = error as NSError + fatalError("Unresolved error \(nserror), \(nserror.userInfo)") + } + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/CircularImageView.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/CircularImageView.swift new file mode 100755 index 0000000..f71c68b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/CircularImageView.swift @@ -0,0 +1,18 @@ +// +// CircularImageView.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +@IBDesignable class CircularImageView: UIImageView { + override func layoutSubviews() { + super.layoutSubviews() + layer.cornerRadius = frame.height / 2 + layer.masksToBounds = true + clipsToBounds = true + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/GradientView.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/GradientView.swift new file mode 100755 index 0000000..8e6d2f1 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/GradientView.swift @@ -0,0 +1,28 @@ +// +// GradientView.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +@IBDesignable class GradientView: UIView { + + private var gradientLayer: CAGradientLayer! + + private var topColor: UIColor = UIColor.Common.viewBackground + private var bottomColor: UIColor = UIColor.Common.tint.withAlphaComponent(0.5) + + override class var layerClass: AnyClass { + return CAGradientLayer.self + } + + override func layoutSubviews() { + self.gradientLayer = self.layer as? CAGradientLayer + self.gradientLayer.colors = [topColor.cgColor, bottomColor.cgColor] + self.gradientLayer.startPoint = CGPoint(x: 0, y: 0) + self.gradientLayer.endPoint = CGPoint(x: 0, y: 1) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/ImagePicker.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/ImagePicker.swift new file mode 100755 index 0000000..80e12e8 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/CustomControls/ImagePicker.swift @@ -0,0 +1,68 @@ +// +// ImagePicker.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +protocol ImagePickerDelegate: class { + func didFinishPickingImage(_ image: UIImage?) +} + +class ImagePicker: NSObject { + weak var viewController: UIViewController! + let imagePickerController = UIImagePickerController() + weak var delegate: ImagePickerDelegate? + + init(from viewController: UIViewController) { + self.viewController = viewController + } + + func showImagePickerSources(sender: UIView) { + let alert = UIAlertController(title: "Choose Image", message: nil, preferredStyle: .actionSheet) + if UIImagePickerController.isSourceTypeAvailable(.camera) { + alert.addAction(UIAlertAction(title: "Camera", style: .default, handler: { _ in + self.openImagePickerController(source: .camera) + })) + } + + alert.addAction(UIAlertAction(title: "Gallery", style: .default, handler: { _ in + self.openImagePickerController(source: .photoLibrary) + })) + + alert.addAction(UIAlertAction.init(title: "Cancel", style: .cancel, handler: nil)) + + switch UIDevice.current.userInterfaceIdiom { + case .pad: + alert.popoverPresentationController?.sourceView = sender + alert.popoverPresentationController?.sourceRect = sender.bounds + alert.popoverPresentationController?.permittedArrowDirections = .up + default: + break + } + + viewController.present(alert, animated: true, completion: nil) + } + + func openImagePickerController(source: UIImagePickerController.SourceType) { + imagePickerController.delegate = self + imagePickerController.sourceType = source + imagePickerController.allowsEditing = true + viewController.present(imagePickerController, animated: true, completion: nil) + } +} + +extension ImagePicker: UIImagePickerControllerDelegate, UINavigationControllerDelegate { + func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) { + if let image = info[UIImagePickerController.InfoKey.editedImage] as? UIImage { + delegate?.didFinishPickingImage(image) + } else { + delegate?.didFinishPickingImage(nil) + } + imagePickerController.dismiss(animated: true, completion: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/NSObjectExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/NSObjectExtension.swift new file mode 100755 index 0000000..142a6c5 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/NSObjectExtension.swift @@ -0,0 +1,15 @@ +// +// NSObjectExtension.swift +// GJAssignment +// +// Created by Anonymous on 16/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +extension NSObject { + class var name: String { + return String(describing: self) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIAlertControllerExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIAlertControllerExtension.swift new file mode 100755 index 0000000..312aea1 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIAlertControllerExtension.swift @@ -0,0 +1,18 @@ +// +// UIAlertViewController.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +extension UIAlertController { + static func show(_ message: String, from viewController: UIViewController) { + let alert = UIAlertController(title: nil, message: message, preferredStyle: .alert) + alert.addAction(.init(title: "OK", style: .cancel, handler: nil)) + viewController.present(alert, animated: true, completion: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIColorExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIColorExtension.swift new file mode 100755 index 0000000..b8c6985 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIColorExtension.swift @@ -0,0 +1,19 @@ +// +// UIColorExtension.swift +// GJAssignment +// +// Created by Anonymous on 16/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +extension UIColor { + struct Common { + static var tint = #colorLiteral(red: 0.3140000105, green: 0.8899999857, blue: 0.7609999776, alpha: 1) + static var text = #colorLiteral(red: 0.2901960784, green: 0.2901960784, blue: 0.2901960784, alpha: 1) + static var tableSeparator = #colorLiteral(red: 0.9411764706, green: 0.9411764706, blue: 0.9411764706, alpha: 1) + static var tableSectionBackground = #colorLiteral(red: 0.9098039216, green: 0.9098039216, blue: 0.9098039216, alpha: 1) + static var viewBackground = #colorLiteral(red: 0.9764705882, green: 0.9764705882, blue: 0.9764705882, alpha: 1) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageExtension.swift new file mode 100755 index 0000000..c8dd676 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageExtension.swift @@ -0,0 +1,26 @@ +// +// UIImageExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +extension UIImage { + struct Contact { + static let placeHolder = #imageLiteral(resourceName: "PlaceholderPhoto") + static let showFavorite = #imageLiteral(resourceName: "HomeFavourite") + } + + struct Action { + static let call = #imageLiteral(resourceName: "CallButton") + static let mail = #imageLiteral(resourceName: "EmailButton") + static let message = #imageLiteral(resourceName: "MessageButton") + static let favorite = #imageLiteral(resourceName: "FavouriteButton") + static let favoriteSelected = #imageLiteral(resourceName: "FavouriteButtonSelected") + static let takePhoto = #imageLiteral(resourceName: "CameraButton") + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageViewExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageViewExtension.swift new file mode 100755 index 0000000..0a140a5 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIImageViewExtension.swift @@ -0,0 +1,17 @@ +// +// UIImageViewExtension.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit + +extension UIImageView { + func makeCircle() { + layer.cornerRadius = bounds.height / 2 + layer.masksToBounds = true + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIViewExtension.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIViewExtension.swift new file mode 100755 index 0000000..f0782aa --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Extensions/UIViewExtension.swift @@ -0,0 +1,21 @@ +// +// UIViewExtension.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import UIKit +import MBProgressHUD + +extension UIView { + func showLoader(show: Bool) { + if show { + MBProgressHUD.showAdded(to: self, animated: true) + } else { + MBProgressHUD.hide(for: self, animated: true) + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Logger/Log.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Logger/Log.swift new file mode 100755 index 0000000..21ceb1b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Utilities/Logger/Log.swift @@ -0,0 +1,27 @@ +// +// Log.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import os + +struct Log { + private static let subsystem = Bundle.main.bundleIdentifier! + private static let appLog = OSLog(subsystem: Log.subsystem, category: "Default") + + static func error(_ msg: String, error: Error? = nil, log: OSLog = appLog) { + os_log("🔥 - %@ %@", log: log, type: .error, msg, error?.localizedDescription ?? "") + } + + static func info(_ msg: String, log: OSLog = appLog) { + os_log("🚀 - %@", log: log, type: .info, msg) + } + + static func debug(_ msg: String, log: OSLog = appLog) { + os_log("👀 - %@", log: log, type: .debug, msg) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/AddContactViewModel/AddContactViewModel.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/AddContactViewModel/AddContactViewModel.swift new file mode 100755 index 0000000..60288bf --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/AddContactViewModel/AddContactViewModel.swift @@ -0,0 +1,121 @@ +// +// AddContactViewModel.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import CoreData + +class AddContactViewModel { + private var httpClient: HTTPClient! + private var editMode: Bool! + + let doneBarButtonTitle = NSLocalizedString("Done", comment: "") + let cancelBarButtonTitle = NSLocalizedString("Cancel", comment: "") + let managedObjectContext = CoreDataManager.shared.managedObjectContext + + var contact: Bindable! + var contactMetadata: [ContactMetadata]! + var isBusy: Bindable = Bindable(false) + var isContactSync: Bindable = Bindable(false) + var error: Bindable = Bindable(nil) + + init(contact: Contact?, client: HTTPClient? = nil) { + self.httpClient = client ?? HTTPClient.shared + + if let contact = contact { + editMode = true + self.contact = Bindable(contact) + self.contactMetadata = self.contact.value.getEditMetaData() + } else { + editMode = false + guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { + fatalError("Failed to decode Contact") + } + + self.contact = Bindable(Contact(entity: entity, insertInto: nil)) + self.contactMetadata = self.contact.value.getEditMetaData() + } + } + + func syncContact() { + //Validate Contact + if !validateContact() { return } + + var request: RequestProtocol = ContactAPI.addContact(contact: contact.value) + if editMode { + request = ContactAPI.updateContact(id: Int(contact.value.id), contact: contact.value) + } + + isBusy.value = true + httpClient.dataTask(request) { [weak self] (result) in + guard let self = self else { + return + } + + self.isBusy.value = false + switch result { + case .success(let data): + guard let data = data else { + return + } + + do { + let contact = try JSONDecoder().decode(Contact.self, from: data) + CoreDataManager.shared.saveContext() + self.isContactSync.value = true + self.contact.value = contact + } catch { + self.error.value = GJError(error.localizedDescription) + Log.error("Unable to decode contact.", error: error) + } + case .failure(let error): + self.error.value = GJError(error.localizedDescription) + Log.error("Error in contact sync.", error: error) + } + } + } + + private func validateContact() -> Bool { + guard let firstName = contact.value.firstName else { + error.value = GJError(NSLocalizedString("Please enter first name.", comment: "")) + return false + } + + if firstName.count < 2 { + let message = NSLocalizedString("First name is too short (minimum is 2 characters)", comment: "") + error.value = GJError(message) + return false + } + + guard let lastName = contact.value.lastName else { + error.value = GJError(NSLocalizedString("Please enter last name.", comment: "")) + return false + } + + if lastName.count < 2 { + let message = NSLocalizedString("Last name is too short (minimum is 2 characters)", comment: "") + error.value = GJError(message) + return false + } + + if let mobile = contact.value.phoneNumber, mobile.count < 10 { + let message = NSLocalizedString("Please enter a vailid mobile number. Mobile number should be of 10 digits or more.", comment: "") + error.value = GJError(message) + return false + } + + if let email = contact.value.email { + let emailRegEx = "[A-Z0-9a-z._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}" + let emailPredicate = NSPredicate(format: "SELF MATCHES %@", emailRegEx) + if !emailPredicate.evaluate(with: email) { + error.value = GJError(NSLocalizedString("Please enter a valid email.", comment: "")) + return false + } + } + return true + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactDetailsViewModel/ContactDetailsViewModel.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactDetailsViewModel/ContactDetailsViewModel.swift new file mode 100755 index 0000000..f99c081 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactDetailsViewModel/ContactDetailsViewModel.swift @@ -0,0 +1,121 @@ +// +// ContactDetailsViewModel.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +class ContactDetailsViewModel { + private var httpClient: HTTPClient! + + let editBarButtonTitle = NSLocalizedString("Edit", comment: "") + + var contact: Bindable! + var isBusy: Bindable = Bindable(false) + var error: Bindable = Bindable(nil) + + // MARK: - Computed Properties + var contactMetadata: [ContactMetadata] { + return contact.value.getDetailsMetadata() + } + + var name: String { + return contact.value.fullName + } + + var imageURL: URL? { + return URL(string: contact.value.profilePic ?? "") + } + + var isFavorite: Bool { + return contact.value.favorite + } + + var telURL: URL? { + if let phone = contact.value.phoneNumber { + return URL(string: String(format: "tel://%@", phone)) + } + return nil + } + + var messageURL: URL? { + if let phone = contact.value.phoneNumber { + return URL(string: String(format: "sms://%@", phone)) + } + return nil + } + + var mailURL: URL? { + if let email = contact.value.email { + return URL(string: String(format: "mailto://%@", email)) + } + return nil + } + + init(contact: Contact, client: HTTPClient? = nil) { + self.contact = Bindable(contact) + self.httpClient = client ?? HTTPClient.shared + } + + func getContactDetails() { + isBusy.value = true + httpClient.dataTask(ContactAPI.getContact(id: Int(contact.value.id))) { [weak self] (result) in + guard let self = self else { + return + } + + self.isBusy.value = false + switch result { + case .success(let data): + guard let data = data else { + return + } + + do { + let contact = try JSONDecoder().decode(Contact.self, from: data) + CoreDataManager.shared.saveContext() + self.contact.value = contact + } catch { + Log.error("Unable to decode Contact.", error: error) + } + case .failure(let error): + self.error.value = GJError(error.localizedDescription) + Log.error("Error in fetching Contact.", error: error) + } + } + } + + func updateFavourite() { + contact.value.favorite = !contact.value.favorite + let contactRequest = ContactAPI.updateContact(id: Int(contact.value.id), contact: contact.value) + isBusy.value = true + httpClient.dataTask(contactRequest) { [weak self] (result) in + guard let self = self else { + return + } + + self.isBusy.value = false + switch result { + case .success(let data): + guard let data = data else { + return + } + + do { + let contact = try JSONDecoder().decode(Contact.self, from: data) + CoreDataManager.shared.saveContext() + self.contact.value = contact + } catch { + Log.error("Unable to decode Contact.", error: error) + } + case .failure(let error): + self.error.value = GJError(error.localizedDescription) + self.contact.value.favorite = !self.contact.value.favorite + Log.error("Error in updating Contact.", error: error) + } + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactListViewModel.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactListViewModel.swift new file mode 100755 index 0000000..352268e --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactListViewModel.swift @@ -0,0 +1,55 @@ +// +// ContactListViewModel.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +class ContactListViewModel { + + private var httpClient: HTTPClient! + + let title = NSLocalizedString("Contact", comment: "") + let groupBarButtonTitle = NSLocalizedString("Groups", comment: "") + + var isBusy: Bindable = Bindable(false) + var contacts: Bindable<[Contact]?> = Bindable(nil) + var error: Bindable = Bindable(nil) + + init(client: HTTPClient? = nil) { + self.httpClient = client ?? HTTPClient.shared + } + + func getContacts() { + isBusy.value = true + httpClient.dataTask(ContactAPI.getContacts) { [weak self] (result) in + guard let self = self else { + return + } + + self.isBusy.value = false + switch result { + case .success(let data): + guard let data = data else { + return + } + + do { + Contact.deleteAllContacts() + let contacts = try JSONDecoder().decode([Contact].self, from: data) + CoreDataManager.shared.saveContext() + self.contacts.value = contacts + Log.info("Contact sync successfully.") + } catch { + Log.error("Unable to decode Contact List", error: error) + } + case .failure(let error): + self.error.value = GJError(error.localizedDescription) + Log.error("Error in fetching Contacts", error: error) + } + } + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactViewModel.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactViewModel.swift new file mode 100755 index 0000000..f078ec6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/ViewModels/ContactListViewModel/ContactViewModel.swift @@ -0,0 +1,25 @@ +// +// ContactViewModel.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation + +class ContactViewModel { + private let contact: Contact! + + let name: String + let imageURL: URL? + let isFavorite: Bool + + init(contact: Contact) { + self.contact = contact + + name = contact.fullName + imageURL = URL(string: contact.profilePic ?? "") + isFavorite = contact.favorite + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.swift new file mode 100755 index 0000000..f0dd4b4 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.swift @@ -0,0 +1,34 @@ +// +// ContactDetailsTableViewCell.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +class ContactDetailsTableViewCell: UITableViewCell { + @IBOutlet weak var descLabel: UILabel! + @IBOutlet weak var infoLabel: UILabel! + + static let identifier = ContactDetailsTableViewCell.name + static let nib = UINib(nibName: ContactDetailsTableViewCell.name, bundle: nil) + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + func config(metaData: ContactMetadata) { + descLabel.text = metaData.desc + infoLabel.text = metaData.info + } + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.xib new file mode 100755 index 0000000..f940098 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactDetailsTableViewCell/ContactDetailsTableViewCell.xib @@ -0,0 +1,63 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.swift new file mode 100755 index 0000000..55332f2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.swift @@ -0,0 +1,52 @@ +// +// ContactEditTableViewCell.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +protocol ContactEditTableViewCellDelegate: class { + func textChanged(contactMetaData: ContactMetadata, text: String) +} + +class ContactEditTableViewCell: UITableViewCell { + + @IBOutlet weak var descLabel: UILabel! + @IBOutlet weak var infoTextField: BindableTextField! { + didSet { + infoTextField.bind {[weak self] in + guard let self = self else { + return + } + self.delegate?.textChanged(contactMetaData: self.contactMetadata, text: $0) + } + } + } + + var contactMetadata: ContactMetadata! + weak var delegate: ContactEditTableViewCellDelegate? + + static let identifier = ContactEditTableViewCell.name + static let nib = UINib(nibName: ContactEditTableViewCell.name, bundle: nil) + + override func awakeFromNib() { + super.awakeFromNib() + // Initialization code + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + // Configure the view for the selected state + } + + func config(metaData: ContactMetadata) { + contactMetadata = metaData + descLabel.text = metaData.desc + infoTextField.text = metaData.info + infoTextField.keyboardType = metaData.keyboardType + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.xib new file mode 100755 index 0000000..6b12e56 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewCell/ContactEditTableViewCell.xib @@ -0,0 +1,69 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.swift new file mode 100755 index 0000000..5769aa8 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.swift @@ -0,0 +1,34 @@ +// +// ContactEditTableViewHeader.swift +// GJAssignment +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +protocol ContactEditTableViewHeaderDelegate: class { + func takeImageButtonAction(_ sender: UIButton) +} + +class ContactEditTableViewHeader: UIView { + @IBOutlet weak var imageView: CircularImageView! + @IBOutlet weak var takeImageButton: UIButton! + + weak var delegate: ContactEditTableViewHeaderDelegate? + + class func get(image: UIImage = UIImage.Contact.placeHolder) -> ContactEditTableViewHeader { + guard let view = Bundle.main.loadNibNamed(ContactEditTableViewHeader.name, + owner: nil, options: nil)? + .first as? ContactEditTableViewHeader else { + fatalError("Unable to load nib ContactEditTableViewHeader.") + } + view.imageView.image = image + return view + } + + @IBAction func takeImageButtonAction(_ sender: UIButton) { + delegate?.takeImageButtonAction(sender) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.xib new file mode 100755 index 0000000..68be86b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactEditTableViewHeader/ContactEditTableViewHeader.xib @@ -0,0 +1,67 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.swift b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.swift new file mode 100755 index 0000000..a749b71 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.swift @@ -0,0 +1,39 @@ +// +// ContactTableViewCell.swift +// GJAssignment +// +// Created by Anonymous on 17/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import UIKit + +class ContactTableViewCell: UITableViewCell { + @IBOutlet weak var contactImageView: UIImageView! + @IBOutlet weak var contactName: UILabel! + @IBOutlet weak var favoriteImageView: UIImageView! + + static let identifier = ContactTableViewCell.name + static let nib = UINib(nibName: ContactTableViewCell.name, bundle: nil) + + override func awakeFromNib() { + super.awakeFromNib() + } + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + } + + override func layoutSubviews() { + super.layoutSubviews() + contactImageView.makeCircle() + } + + func config(contact: Contact) { + let viewModel = ContactViewModel(contact: contact) + contactName.text = viewModel.name + contactImageView.image = UIImage.Contact.placeHolder + favoriteImageView.isHidden = !viewModel.isFavorite + favoriteImageView.image = viewModel.isFavorite ? UIImage.Contact.showFavorite : nil + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.xib b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.xib new file mode 100755 index 0000000..3e16745 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignment/Views/ContactTableViewCell/ContactTableViewCell.xib @@ -0,0 +1,75 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/AddContactTests/AddContactTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/AddContactTests/AddContactTests.swift new file mode 100755 index 0000000..e80811d --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/AddContactTests/AddContactTests.swift @@ -0,0 +1,78 @@ +// +// AddContactViewModelTest.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class AddContactTests: XCTestCase { + + var addContactViewModel: AddContactViewModel! + + override func setUp() { + let session = MockSession() + let client = HTTPClient(session: session) + addContactViewModel = AddContactViewModel(contact: nil, client: client) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testCancelBarButtonTitle() { + XCTAssert(addContactViewModel.cancelBarButtonTitle == "Cancel", "Cancel button title mismatch.") + } + + func testDoneBarButtonTitle() { + XCTAssert(addContactViewModel.doneBarButtonTitle == "Done", "Done button title mismatch.") + } + + func testAddContact() { + addContactViewModel.contact.value = MockContact.getComplete() + + let expectation = self.expectation(description: "No response recevice from contact list API.") + + addContactViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + addContactViewModel.isContactSync.bind { (isSync) in + if isSync { + expectation.fulfill() + } + } + + addContactViewModel.syncContact() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + + func testAddContactResponse() { + addContactViewModel.contact.value = MockContact.getComplete() + + var isContactSync = false + let expectation = self.expectation(description: "No response recevice from contact list API.") + + addContactViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + addContactViewModel.isContactSync.bind { (isSync) in + isContactSync = isSync + } + addContactViewModel.contact.bind { (contact) in + if isContactSync { + XCTAssert(contact.firstName == "Anonymous", "Contact fist name mismatch.") + XCTAssert(contact.lastName == "Anonymous", "Contact last name mismatch.") + XCTAssert(contact.phoneNumber == "+910987654321", "Contact phone number mismatch.") + XCTAssert(contact.email == "vc@gmail.com", "Contact phone number mismatch.") + XCTAssert(contact.favorite == false, "Contact favorite status mismatch.") + expectation.fulfill() + } + } + + addContactViewModel.syncContact() + self.waitForExpectations(timeout: 10.0, handler: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsErrorTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsErrorTests.swift new file mode 100755 index 0000000..368fa8a --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsErrorTests.swift @@ -0,0 +1,42 @@ +// +// ContactDetailsErrorTests.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class ContactDetailsErrorTests: XCTestCase { + + var contact: Contact! + var contactDetailsViewModel: ContactDetailsViewModel! + + override func setUp() { + contact = MockContact.getPartial() + + let session = BadMockSession() + let client = HTTPClient(session: session) + contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) + } + + override func tearDown() { + + } + + func testContactsAPIFailedResponse() { + let expectation = self.expectation(description: "No error returns by contact API.") + + contactDetailsViewModel.error.bind { (error) in + if error != nil { + expectation.fulfill() + } + } + + contactDetailsViewModel.getContactDetails() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsTests.swift new file mode 100755 index 0000000..fa208b6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsTests.swift @@ -0,0 +1,67 @@ +// +// ContactDetailsViewModelTest.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class ContactDetailsTests: XCTestCase { + + var contact: Contact! + var contactDetailsViewModel: ContactDetailsViewModel! + + override func setUp() { + contact = MockContact.getPartial() + + let session = MockSession() + let client = HTTPClient(session: session) + contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) + } + + override func tearDown() { + + } + + func testEditBarButtonTitle() { + XCTAssert(contactDetailsViewModel.editBarButtonTitle == "Edit", "Contact edit button title mismatch.") + } + + func testContactAPIResponse() { + let expectation = self.expectation(description: "No response recevice from contact list API.") + + contactDetailsViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + + contactDetailsViewModel.contact.bind { (contact) in + if contact.phoneNumber != nil { + expectation.fulfill() + } + } + + contactDetailsViewModel.getContactDetails() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + + func testUpdateContactFavoriteStatus() { + let expectation = self.expectation(description: "No response recevice from contact list API.") + let oldStatus = contact.favorite + + contactDetailsViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + + contactDetailsViewModel.contact.bind { (contact) in + if contact.favorite != oldStatus { + expectation.fulfill() + } + } + + contactDetailsViewModel.updateFavourite() + self.waitForExpectations(timeout: 100.0, handler: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsViewModelTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsViewModelTests.swift new file mode 100755 index 0000000..e1752c6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactDetailsTests/ContactDetailsViewModelTests.swift @@ -0,0 +1,38 @@ +// +// ContactDetailsViewModelTests.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class ContactDetailsViewModelTests: XCTestCase { + + var contact: Contact! + var contactDetailsViewModel: ContactDetailsViewModel! + + override func setUp() { + contact = MockContact.getComplete() + + let session = MockSession() + let client = HTTPClient(session: session) + contactDetailsViewModel = ContactDetailsViewModel(contact: contact, client: client) + } + + override func tearDown() { + + } + + func testCheckContactViewModel() { + XCTAssert(contactDetailsViewModel.name == "Anonymous", "Contact full name mismatch.") + XCTAssert(contactDetailsViewModel.imageURL == URL(string: "image.png"), "Contact image url mismatch.") + XCTAssert(contactDetailsViewModel.isFavorite == false, "Contact favorite status mismatch.") + XCTAssert(contactDetailsViewModel.telURL == URL(string: "tel://+910987654321"), "Contact phone number url mismatch.") + XCTAssert(contactDetailsViewModel.messageURL == URL(string: "sms://+910987654321"), "Contact message url mismatch.") + XCTAssert(contactDetailsViewModel.mailURL == URL(string: "mailto://vc@gmail.com"), "Contact email url mismatch.") + } + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListErrorTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListErrorTests.swift new file mode 100755 index 0000000..dc136cf --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListErrorTests.swift @@ -0,0 +1,38 @@ +// +// ContactListErrorTests.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class ContactListErrorTests: XCTestCase { + + var contactListViewModel: ContactListViewModel! + + override func setUp() { + let session = BadMockSession() + let client = HTTPClient(session: session) + contactListViewModel = ContactListViewModel(client: client) + } + + override func tearDown() { + + } + + func testContactsAPIFailedResponse() { + let expectation = self.expectation(description: "No error return by API.") + + contactListViewModel.error.bind { (error) in + if error != nil { + expectation.fulfill() + } + } + + contactListViewModel.getContacts() + self.waitForExpectations(timeout: 10.0, handler: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListTests.swift new file mode 100755 index 0000000..a5b6a71 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactListTests.swift @@ -0,0 +1,92 @@ +// +// ContactListViewModelTest.swift +// GJAssignmentTests +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +import CoreData +@testable import GJAssignment + +class ContactListTests: XCTestCase { + + var contactListViewModel: ContactListViewModel! + + override func setUp() { + let session = MockSession() + let client = HTTPClient(session: session) + contactListViewModel = ContactListViewModel(client: client) + } + + override func tearDown() { + + } + + func testContactListTitle() { + XCTAssert(contactListViewModel.title == "Contact", "Contact list title mismatch.") + } + + func testGroupBarButtonTitle() { + XCTAssert(contactListViewModel.groupBarButtonTitle == "Groups", "Contact list title mismatch.") + } + + func testContactsAPISuccessResponse() { + let expectation = self.expectation(description: "No response recevice from contact list API.") + + contactListViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + + contactListViewModel.contacts.bind { (contacts) in + if contacts != nil { + expectation.fulfill() + } + } + + contactListViewModel.getContacts() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + + func testContactsAPIResultCount() { + let expectation = self.expectation(description: "Invalid number of contact returns by contact list API.") + + contactListViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + + contactListViewModel.contacts.bind { (contacts) in + if contacts?.count == 3 { + expectation.fulfill() + } + } + + contactListViewModel.getContacts() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + + func testConactsAPIObject() { + let expectation = self.expectation(description: "Invalid number of contact returns by contact list API.") + + contactListViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + + contactListViewModel.contacts.bind { (contacts) in + if let contact = contacts?[0] { + XCTAssert(contact.sectionTitle == "V", "Contact section title is incorrect.") + XCTAssert(contact.fullName == "Anonymous", "Contact full name is incorrect.") + XCTAssert(contact.firstName == "Anonymous", "Contact first name is incorrect.") + XCTAssert(contact.lastName == "Anonymous", "Contact last name is incorrect.") + XCTAssert(contact.favorite == false, "Contact favorite status is incorrect.") + XCTAssert(contact.phoneNumber == nil, "Contact phone number must be nil.") + XCTAssert(contact.email == nil, "Contact email must be nil.") + expectation.fulfill() + } + } + + contactListViewModel.getContacts() + self.waitForExpectations(timeout: 10.0, handler: nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactViewModelTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactViewModelTests.swift new file mode 100755 index 0000000..1777516 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/ContactListTests/ContactViewModelTests.swift @@ -0,0 +1,38 @@ +// +// GJAssignmentTests.swift +// GJAssignmentTests +// +// Created by Anonymous on 14/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class ContactViewModelTests: XCTestCase { + + var contact: Contact! + var contactViewModel: ContactViewModel! + + override func setUp() { + contact = MockContact.getPartial() + contactViewModel = ContactViewModel(contact: contact) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testContactFullName() { + XCTAssert(contactViewModel.name == "Anonymous", "Complete name mismatch.") + } + + func testFavoriteContact() { + XCTAssert(contactViewModel.isFavorite == false, "Contact was favorite but view model return contact is not favorite") + } + + func testProfilePicURL() { + let testURL = URL(string: "image.png") + XCTAssert(contactViewModel.imageURL == testURL, "Contact profile URL mismatch.") + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/Info.plist b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/Info.plist new file mode 100755 index 0000000..6c40a6c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockContact.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockContact.swift new file mode 100755 index 0000000..bf492fa --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockContact.swift @@ -0,0 +1,41 @@ +// +// MockContact.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +import CoreData + +@testable import GJAssignment + +struct MockContact { + static func getEmpty() -> Contact { + let managedObjectContext = CoreDataManager.shared.managedObjectContext + guard let entity = NSEntityDescription.entity(forEntityName: Contact.name, in: managedObjectContext) else { + fatalError("Unable to create Contact entity.") + } + + let contact = Contact(entity: entity, insertInto: nil) + return contact + } + + static func getPartial() -> Contact { + let contact = getEmpty() + contact.id = 1 + contact.firstName = "Anonymous" + contact.lastName = "Anonymous" + contact.profilePic = "image.png" + contact.favorite = false + return contact + } + + static func getComplete() -> Contact { + let contact = getPartial() + contact.phoneNumber = "+910987654321" + contact.email = "vc@gmail.com" + return contact + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockRequest.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockRequest.swift new file mode 100755 index 0000000..3c5f9d0 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/MockRequest.swift @@ -0,0 +1,133 @@ +// +// MockRequest.swift +// GJAssignmentTests +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +@testable import GJAssignment + +typealias Completion = (Data?, URLResponse?, Error?) -> Void + +enum MockRequest { + case contacts + case singlecontact +} + +extension MockRequest { + //Identify request based on endpoint + static func identifyRequest(request: URLRequest) -> MockRequest? { + if request.url?.path == "/contacts.json" { + return contacts + } else if request.url?.path == "/contacts/1.json" { + return singlecontact + } + return nil + } + + //Return success response + func completionHandler(request: URLRequest, completion: Completion) { + guard let method = request.httpMethod, let httpMethod = HTTPMethod(rawValue: method) else { + fatalError("Unknown HTTPMethod Used.") + } + + switch (self, httpMethod) { + case (.contacts, .get): + getContacts(request: request, statusCode: 200, completion: completion) + case (.contacts, .post): + postContact(request: request, statusCode: 200, completion: completion) + case (.singlecontact, .get): + getContact(request: request, statusCode: 200, completion: completion) + case (.singlecontact, .put): + getFavoriteContact(request: request, statusCode: 200, completion: completion) + case (.singlecontact, .delete): + getContact(request: request, statusCode: 204, completion: completion) + default: + fatalError("Request not handled yet.") + } + } + + //Return error response + func badCompletionHandler(request: URLRequest, completion: Completion) { + guard let method = request.httpMethod, let httpMethod = HTTPMethod(rawValue: method) else { + fatalError("Unknown HTTPMethod Used.") + } + + switch (self, httpMethod) { + case (.contacts, .get): + getBadContacts(request: request, statusCode: 400, completion: completion) + case (.singlecontact, .get): + getBadContact(request: request, statusCode: 400, completion: completion) + default: + fatalError("Request not handled yet.") + } + } + + // MARK: - Helper Functions + private func getContacts(request: URLRequest, statusCode: Int, completion: Completion) { + let path = Bundle.init(for: MockSession.self).path(forResource: "contacts", ofType: "json") + guard let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe) else { + let error = NSError(domain: "No stub data foubt", code: 0, userInfo: nil) + completion(nil, nil, error) + return + } + + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(data, response, nil) + } + + private func getBadContacts(request: URLRequest, statusCode: Int, completion: Completion) { + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(nil, response, GJError("Server not reachable.")) + } + + private func postContact(request: URLRequest, statusCode: Int, completion: Completion) { + let path = Bundle.init(for: MockSession.self).path(forResource: "contact", ofType: "json") + guard let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe) else { + let error = NSError(domain: "No stub data foubt", code: 0, userInfo: nil) + completion(nil, nil, error) + return + } + + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(data, response, nil) + } + + private func getContact(request: URLRequest, statusCode: Int, completion: Completion) { + let path = Bundle.init(for: MockSession.self).path(forResource: "contact", ofType: "json") + guard let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe) else { + let error = NSError(domain: "No stub data foubt", code: 0, userInfo: nil) + completion(nil, nil, error) + return + } + + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(data, response, nil) + } + + private func getBadContact(request: URLRequest, statusCode: Int, completion: Completion) { + let path = Bundle.init(for: MockSession.self).path(forResource: "not_found", ofType: "json") + guard let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe) else { + let error = NSError(domain: "No stub data foubt", code: 0, userInfo: nil) + completion(nil, nil, error) + return + } + + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(data, response, nil) + } + + private func getFavoriteContact(request: URLRequest, statusCode: Int, completion: Completion) { + let path = Bundle.init(for: MockSession.self).path(forResource: "fav_contact", ofType: "json") + guard let data = try? Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe) else { + let error = NSError(domain: "No stub data foubt", code: 0, userInfo: nil) + completion(nil, nil, error) + return + } + + let response = HTTPURLResponse(url: request.url!, statusCode: statusCode, httpVersion: nil, headerFields: nil) + completion(data, response, nil) + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contact.json b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contact.json new file mode 100755 index 0000000..30c3859 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contact.json @@ -0,0 +1,11 @@ +{ + "id": 1, + "first_name": "Anonymous", + "last_name": "Anonymous", + "email": "vc@gmail.com", + "phone_number": "+910987654321", + "profile_pic": "/images/missing.png", + "favorite": false, + "created_at": "2019-08-17T18:45:58.243Z", + "updated_at": "2019-08-17T18:45:58.243Z" +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contacts.json b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contacts.json new file mode 100755 index 0000000..d19fb67 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/contacts.json @@ -0,0 +1,27 @@ +[ + { + "id": 1, + "first_name": "Anonymous", + "last_name": "Anonymous", + "profile_pic": "/images/missing.png", + "favorite": false, + "url": "https://gojek-contacts-app.herokuapp.com/contacts/1.json" + }, + { + "id": 2, + "first_name": "Hello", + "last_name": "There", + "profile_pic": "/images/missing.png", + "favorite": false, + "url": "https://gojek-contacts-app.herokuapp.com/contacts/2.json" + }, + { + "id": 3, + "first_name": "Go", + "last_name": "Jek", + "profile_pic": "/images/missing.png", + "favorite": true, + "url": "https://gojek-contacts-app.herokuapp.com/contacts/3.json" + } +] + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/fav_contact.json b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/fav_contact.json new file mode 100755 index 0000000..cb3d780 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/fav_contact.json @@ -0,0 +1,11 @@ +{ + "id": 1, + "first_name": "Anonymous", + "last_name": "Anonymous", + "email": "vc@gmail.com", + "phone_number": "+910987654321", + "profile_pic": "/images/missing.png", + "favorite": true, + "created_at": "2019-08-17T18:45:58.243Z", + "updated_at": "2019-08-17T18:45:58.243Z" +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/not_found.json b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/not_found.json new file mode 100755 index 0000000..69f3922 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockData/StubJSON/not_found.json @@ -0,0 +1,4 @@ +{ + "status": "404", + "error": "Not Found" +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/BadMockSession.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/BadMockSession.swift new file mode 100755 index 0000000..38cdae6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/BadMockSession.swift @@ -0,0 +1,22 @@ +// +// BadMockSession.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +@testable import GJAssignment + +//BadMockSession will always return error or nil response +class BadMockSession: URLSessionProtocol { + func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { + + if let mockRequest = MockRequest.identifyRequest(request: request) { + mockRequest.badCompletionHandler(request: request, completion: completionHandler) + } + + return MockDataTask() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockDataTask.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockDataTask.swift new file mode 100755 index 0000000..7c58337 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockDataTask.swift @@ -0,0 +1,18 @@ +// +// MokeDataTask.swift +// GJAssignmentTests +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +@testable import GJAssignment + +class MockDataTask: URLSessionDataTaskProtocol { + func resume() { + } + + func cancel() { + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockSession.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockSession.swift new file mode 100755 index 0000000..4598f4e --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/MockNetworking/MockSession.swift @@ -0,0 +1,22 @@ +// +// MockSession.swift +// GJAssignmentTests +// +// Created by Anonymous on 18/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import Foundation +@testable import GJAssignment + +//MokeSession or Good MockSession always return success response +class MockSession: URLSessionProtocol { + func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTaskProtocol { + + if let mockRequest = MockRequest.identifyRequest(request: request) { + mockRequest.completionHandler(request: request, completion: completionHandler) + } + + return MockDataTask() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/UpdateContactTests/UpdateContactTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/UpdateContactTests/UpdateContactTests.swift new file mode 100755 index 0000000..0afd52c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentTests/UpdateContactTests/UpdateContactTests.swift @@ -0,0 +1,71 @@ +// +// UpdateContactTests.swift +// GJAssignmentTests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest +@testable import GJAssignment + +class UpdateContactTests: XCTestCase { + + var contact: Contact! + var addContactViewModel: AddContactViewModel! + + override func setUp() { + let session = MockSession() + let client = HTTPClient(session: session) + contact = MockContact.getComplete() + addContactViewModel = AddContactViewModel(contact: contact, client: client) + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testAddContact() { + let expectation = self.expectation(description: "No response recevice from contact list API.") + + addContactViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + addContactViewModel.isContactSync.bind { (isSync) in + if isSync { + expectation.fulfill() + } + } + + addContactViewModel.syncContact() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + + func testAddContactResponse() { + addContactViewModel.contact.value = MockContact.getComplete() + + var isContactSync = false + let expectation = self.expectation(description: "No response recevice from contact list API.") + + addContactViewModel.error.bind { (error) in + XCTAssert(error == nil, error!.localizedDescription) + } + addContactViewModel.isContactSync.bind { (isSync) in + isContactSync = isSync + } + addContactViewModel.contact.bind { (contact) in + if isContactSync { + XCTAssert(contact.firstName == "Anonymous", "Contact fist name mismatch.") + XCTAssert(contact.lastName == "Anonymous", "Contact last name mismatch.") + XCTAssert(contact.phoneNumber == "+910987654321", "Contact phone number mismatch.") + XCTAssert(contact.email == "vc@gmail.com", "Contact phone number mismatch.") + XCTAssert(contact.favorite == true, "Contact favorite status mismatch.") + expectation.fulfill() + } + } + + addContactViewModel.syncContact() + self.waitForExpectations(timeout: 10.0, handler: nil) + } + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/CustomControlTests/ImagePickerTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/CustomControlTests/ImagePickerTests.swift new file mode 100755 index 0000000..95cbba2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/CustomControlTests/ImagePickerTests.swift @@ -0,0 +1,81 @@ +// +// ImagePickerTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class ImagePickerTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testImagePickerActionSheet() { + openImagePicker() + + let chooseImageSheet = app.sheets["Choose Image"] + XCTAssertTrue(chooseImageSheet.exists, "Action sheet not presented.") + + //Camera button will not be available in Simulator + if !isSimulartor() { + let cameraButton = chooseImageSheet.buttons["Camera"] + XCTAssertTrue(cameraButton.exists, "Camera button not available") + } + + let galleryButton = chooseImageSheet.buttons["Gallery"] + XCTAssertTrue(galleryButton.exists, "Gallery button not available") + + let cancelButton = chooseImageSheet.buttons["Cancel"] + XCTAssertTrue(cancelButton.exists, "Cancel button not available") + } + + func testCameraImage() { + openImagePicker() + + let chooseImageSheet = app.sheets["Choose Image"] + XCTAssertTrue(chooseImageSheet.exists, "Action sheet not presented.") + + if isSimulartor() { + return + } + + let cameraButton = chooseImageSheet.buttons["Camera"] + cameraButton.tap() + + let permissionAlert = app.alerts["“GJAssignment” Would Like to Access the Camera"] + if permissionAlert.exists { + let allowPermissionButton = permissionAlert.buttons["OK"] + allowPermissionButton.tap() + } + + let photoCaptureButton = app.buttons["PhotoCapture"] + XCTAssertTrue(photoCaptureButton.exists, "Photo capture button not exist.") + photoCaptureButton.tap() + } + + private func openImagePicker() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let tablesQuery = app.tables + let selectimagebuttonButton = tablesQuery.buttons["selectImageButton"] + selectimagebuttonButton.tap() + } + + private func isSimulartor() -> Bool { + return ProcessInfo.processInfo.environment["SIMULATOR_DEVICE_NAME"] != nil + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/Info.plist b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/Info.plist new file mode 100755 index 0000000..6c40a6c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/Info.plist @@ -0,0 +1,22 @@ + + + + + CFBundleDevelopmentRegion + $(DEVELOPMENT_LANGUAGE) + CFBundleExecutable + $(EXECUTABLE_NAME) + CFBundleIdentifier + $(PRODUCT_BUNDLE_IDENTIFIER) + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + $(PRODUCT_NAME) + CFBundlePackageType + BNDL + CFBundleShortVersionString + 1.0 + CFBundleVersion + 1 + + diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/AddContactNavigationTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/AddContactNavigationTests.swift new file mode 100755 index 0000000..32bfe2f --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/AddContactNavigationTests.swift @@ -0,0 +1,51 @@ +// +// AddContactNavigationTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class AddContactNavigationTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testAddContactNavigationBar() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] + + let cancelButton = addContactNavigationBar.buttons["Cancel"] + XCTAssertTrue(cancelButton.exists, "Cancel button not exists in add contact navigation bar.") + + let doneButton = addContactNavigationBar.buttons["Done"] + XCTAssertTrue(doneButton.exists, "Done button not exists in add contact navigation bar.") + } + + func testAddContactNavigationBarCancelButtonAction() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] + + let cancelButton = addContactNavigationBar.buttons["Cancel"] + cancelButton.tap() + + XCTAssertTrue(contactNavigationBar.isHittable, "Contact list navigation bar not exist after dismising the add contact view controller.") + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactDetailsNavigationTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactDetailsNavigationTests.swift new file mode 100755 index 0000000..4ac7876 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactDetailsNavigationTests.swift @@ -0,0 +1,61 @@ +// +// ContactDetailsNavigationTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class ContactDetailsNavigationTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testContactDetailsNavigation() { + tapContactListFirstCell() + + let contactNavigationBar = app.navigationBars["Contact"] + let backButton = contactNavigationBar.buttons["Contact"] + XCTAssertTrue(backButton.exists, "Back button not exist in navigation bar.") + + let editButton = contactNavigationBar.buttons["Edit"] + XCTAssertTrue(editButton.exists, "Back button not exist in navigation bar.") + } + + func testContactEditNavigationFromContactDetails() { + tapContactListFirstCell() + let contactNavigationBar = app.navigationBars["Contact"] + + let editButton = contactNavigationBar.buttons["Edit"] + XCTAssertTrue(editButton.exists, "Back button not exist in navigation bar.") + + editButton.tap() + + let editNavigationBar = app.navigationBars["GJAssignment.AddContactView"] + XCTAssertTrue(editNavigationBar.exists, "Edit navigation bar not exist.") + } + + private func tapContactListFirstCell() { + let myTable = app.tables.matching(identifier: "contactListTableView") + let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") + + let predicate = NSPredicate(format: "isHittable == true") + let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) + let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) + XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") + + cell.tap() + } + +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactListNavigationTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactListNavigationTests.swift new file mode 100755 index 0000000..cf2a0c2 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/NavigationTests/ContactListNavigationTests.swift @@ -0,0 +1,60 @@ +// +// NavigationTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class NavigationTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testContactListNavigation() { + let contactNavigationBar = app.navigationBars["Contact"] + XCTAssertTrue(contactNavigationBar.exists, "Contact list navigation bar not exist.") + + let groupButton = contactNavigationBar.buttons["Groups"] + XCTAssertTrue(groupButton.exists, "Group button not exists in contact list navigation bar.") + + let addButton = contactNavigationBar.buttons["Add"] + XCTAssertTrue(addButton.exists, "Group button not exists in contact list navigation bar.") + } + + func testAddContactNavigationFromContactList() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] + XCTAssertTrue(addContactNavigationBar.exists, "Add contact navigation bar not exist.") + } + + func testContactDetailsNavigationFromContactList() { + let myTable = app.tables.matching(identifier: "contactListTableView") + let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") + + let predicate = NSPredicate(format: "isHittable == true") + let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) + let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) + XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") + + cell.tap() + + let contactNavigationBar = app.navigationBars["Contact"] + let backButton = contactNavigationBar.buttons["Contact"] + XCTAssertTrue(contactNavigationBar.exists && backButton.exists, "Contact list navigation bar not exist.") + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/AddContactControllerTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/AddContactControllerTests.swift new file mode 100755 index 0000000..1aaefce --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/AddContactControllerTests.swift @@ -0,0 +1,163 @@ +// +// AddContactControllerTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class AddContactControllerTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + //Test if alret is presented when try to insert empty contact + func testAddEmptyContact() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let addContactNavigationBar = app.navigationBars["GJAssignment.AddContactView"] + let doneButton = addContactNavigationBar.buttons["Done"] + doneButton.tap() + + let alertsQuery = app.alerts.firstMatch + XCTAssertTrue(alertsQuery.exists, "No alert present when all field are empty.") + + let alertCancelButton = alertsQuery.buttons["OK"] + XCTAssertTrue(alertCancelButton.exists, "OK button not exists in error alert.") + } + + //Test all elements of add contact view controller is exists + func testAddContactElementsExistence() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let tablesQuery = app.tables + let selectimagebuttonButton = tablesQuery.buttons["selectImageButton"] + XCTAssertTrue(selectimagebuttonButton.exists, "Select image button not exists.") + + let table = app.tables.matching(identifier: "addContactTableView") + let firstNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_0") + XCTAssertTrue(firstNameCell.exists, "First name table view cell not exists.") + + let lastNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_1") + XCTAssertTrue(lastNameCell.exists, "Last name table view cell not exists.") + + let mobileCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_2") + XCTAssertTrue(mobileCell.exists, "Mobile table view cell not exists.") + + let emailCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_3") + XCTAssertTrue(emailCell.exists, "Email table view cell not exists.") + + let firstNameTextField = firstNameCell.textFields["infoTextField"] + XCTAssertTrue(firstNameTextField.exists, "First name text field not exists.") + + let lastNameTextField = lastNameCell.textFields["infoTextField"] + XCTAssertTrue(lastNameTextField.exists, "Last name text field not exists.") + + let mobileTextField = mobileCell.textFields["infoTextField"] + XCTAssertTrue(mobileTextField.exists, "Mobile name text field not exists.") + + let emailTextField = emailCell.textFields["infoTextField"] + XCTAssertTrue(emailTextField.exists, "Email name text field not exists.") + } + + //Test all elements of add contact are not covered by any other view + func testAddContactElementsHittable() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let tablesQuery = app.tables + let selectimagebuttonButton = tablesQuery.buttons["selectImageButton"] + XCTAssertTrue(selectimagebuttonButton.isHittable, "Select image button not hittable.") + + let table = app.tables.matching(identifier: "addContactTableView") + let firstNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_0") + XCTAssertTrue(firstNameCell.isHittable, "First name table view cell not hittable.") + + let lastNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_1") + XCTAssertTrue(lastNameCell.isHittable, "Last name table view cell not hittable.") + + let mobileCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_2") + XCTAssertTrue(mobileCell.isHittable, "Mobile table view cell not hittable.") + + let emailCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_3") + XCTAssertTrue(emailCell.isHittable, "Email table view cell not hittable.") + + let firstNameTextField = firstNameCell.textFields["infoTextField"] + XCTAssertTrue(firstNameTextField.isHittable, "First name text field not hittable.") + + let lastNameTextField = lastNameCell.textFields["infoTextField"] + XCTAssertTrue(lastNameTextField.isHittable, "Last name text field not hittable.") + + let mobileTextField = mobileCell.textFields["infoTextField"] + XCTAssertTrue(mobileTextField.isHittable, "Mobile name text field not hittable.") + + let emailTextField = emailCell.textFields["infoTextField"] + XCTAssertTrue(emailTextField.isHittable, "Email name text field not hittable.") + } + + //Test if all text field can become first responder and match the value after typing + func testAddContactTextFieldEntry() { + let contactNavigationBar = app.navigationBars["Contact"] + let addButton = contactNavigationBar.buttons["Add"] + addButton.tap() + + let table = app.tables.matching(identifier: "addContactTableView") + let firstNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_0") + XCTAssertTrue(firstNameCell.exists, "First name table view cell not exist.") + + let firstNameTextField = firstNameCell.textFields["infoTextField"] + firstNameTextField.tap() + firstNameTextField.typeText("Anonymous") + guard let firstName = firstNameTextField.value as? String else { + XCTFail("Invalid first name.") + return + } + XCTAssertTrue(firstName == "Anonymous", "Incorrect entry in first name field") + + let lastNameCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_1") + let lastNameTextField = lastNameCell.textFields["infoTextField"] + lastNameTextField.tap() + lastNameTextField.typeText("Anonymous") + guard let lastName = lastNameTextField.value as? String else { + XCTFail("Invalid last name.") + return + } + XCTAssertTrue(lastName == "Anonymous", "Incorrect entry in last name field") + + let mobileCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_2") + let mobileTextField = mobileCell.textFields["infoTextField"] + mobileTextField.tap() + mobileTextField.typeText("+910987654321") + guard let mobileNumber = mobileTextField.value as? String else { + XCTFail("Invalid mobile number.") + return + } + XCTAssertTrue(mobileNumber == "+910987654321", "Incorrect entry in mobile field") + + let emailCell = table.cells.element(matching: .cell, identifier: "editTableViewCell_3") + let emailTextField = emailCell.textFields["infoTextField"] + emailTextField.tap() + emailTextField.typeText("vc@gmail.com") + guard let email = emailTextField.value as? String else { + XCTFail("Invalid email.") + return + } + XCTAssertTrue(email == "vc@gmail.com", "Incorrect entry in email field") + } +} diff --git a/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/ContactDetailsControllerTests.swift b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/ContactDetailsControllerTests.swift new file mode 100755 index 0000000..75f8feb --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/GJAssignmentUITests/ViewControllerTests/ContactDetailsControllerTests.swift @@ -0,0 +1,74 @@ +// +// ContactDetailsControllerTests.swift +// GJAssignmentUITests +// +// Created by Anonymous on 19/08/19. +// Copyright © 2019 Anonymous. All rights reserved. +// + +import XCTest + +class ContactDetailsControllerTests: XCTestCase { + + private var app: XCUIApplication! + + override func setUp() { + continueAfterFailure = false + app = XCUIApplication() + app.launch() + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + } + + func testAddContactElementsExistence() { + tapContactListFirstCell() + + let table = app.tables.matching(identifier: "detailsTableView") + let mobileNumberCell = table.cells.element(matching: .cell, identifier: "detailsTableViewCell_0") + XCTAssertTrue(mobileNumberCell.exists, "Mobile number table view cell not exist.") + + let mobileDescLabel = mobileNumberCell.staticTexts["descLabel"] + XCTAssertTrue(mobileDescLabel.exists, "Mobile number placeholder label not exist.") + + let mobileInfoLabel = mobileNumberCell.staticTexts["infoLabel"] + XCTAssertTrue(mobileInfoLabel.exists, "Mobile number value label not exist.") + + let emailCell = table.cells.element(matching: .cell, identifier: "detailsTableViewCell_1") + XCTAssertTrue(emailCell.exists, "Email table view cell not exist.") + + let emailDescLabel = emailCell.staticTexts["descLabel"] + XCTAssertTrue(emailDescLabel.exists, "Email placeholder label not exist.") + + let emailInfoLabel = emailCell.staticTexts["infoLabel"] + XCTAssertTrue(emailInfoLabel.exists, "Email value label not exist.") + + let contactImageView = app.otherElements.containing(.image, identifier: "contactImageView").firstMatch + XCTAssertTrue(contactImageView.exists, "Contact image view not exist.") + + let callImageView = app.otherElements.containing(.image, identifier: "callActionImage").firstMatch + XCTAssertTrue(callImageView.exists, "Call image view not exist.") + + let msgImageView = app.otherElements.containing(.image, identifier: "messageActionImage").firstMatch + XCTAssertTrue(msgImageView.exists, "Message image view not exist.") + + let emailImageView = app.otherElements.containing(.image, identifier: "emailActionImage").firstMatch + XCTAssertTrue(emailImageView.exists, "Email image view not exist.") + + let favImageView = app.otherElements.containing(.image, identifier: "favoriteActionImage").firstMatch + XCTAssertTrue(favImageView.exists, "Favorite image view not exist.") + } + + private func tapContactListFirstCell() { + let myTable = app.tables.matching(identifier: "contactListTableView") + let cell = myTable.cells.element(matching: .cell, identifier: "contactTableViewCell_0_0") + + let predicate = NSPredicate(format: "isHittable == true") + let expectationEval = expectation(for: predicate, evaluatedWith: cell, handler: nil) + let waiter = XCTWaiter.wait(for: [expectationEval], timeout: 30.0) + XCTAssert(XCTWaiter.Result.completed == waiter, "Failed time out waiting for rate") + + cell.tap() + } +} diff --git a/Go-Jek/GO-JEK-Assignment/Gemfile b/Go-Jek/GO-JEK-Assignment/Gemfile new file mode 100755 index 0000000..7a118b4 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Gemfile @@ -0,0 +1,3 @@ +source "https://rubygems.org" + +gem "fastlane" diff --git a/Go-Jek/GO-JEK-Assignment/Gemfile.lock b/Go-Jek/GO-JEK-Assignment/Gemfile.lock new file mode 100755 index 0000000..9dc60ca --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Gemfile.lock @@ -0,0 +1,159 @@ +GEM + remote: https://rubygems.org/ + specs: + CFPropertyList (3.0.0) + addressable (2.6.0) + public_suffix (>= 2.0.2, < 4.0) + atomos (0.1.3) + babosa (1.0.2) + claide (1.0.3) + colored (1.2) + colored2 (3.1.2) + commander-fastlane (4.4.6) + highline (~> 1.7.2) + declarative (0.0.10) + declarative-option (0.1.0) + digest-crc (0.4.1) + domain_name (0.5.20190701) + unf (>= 0.0.5, < 1.0.0) + dotenv (2.7.5) + emoji_regex (1.0.1) + excon (0.66.0) + faraday (0.15.4) + multipart-post (>= 1.2, < 3) + faraday-cookie_jar (0.0.6) + faraday (>= 0.7.4) + http-cookie (~> 1.0.0) + faraday_middleware (0.13.1) + faraday (>= 0.7.4, < 1.0) + fastimage (2.1.5) + fastlane (2.129.0) + CFPropertyList (>= 2.3, < 4.0.0) + addressable (>= 2.3, < 3.0.0) + babosa (>= 1.0.2, < 2.0.0) + bundler (>= 1.12.0, < 3.0.0) + colored + commander-fastlane (>= 4.4.6, < 5.0.0) + dotenv (>= 2.1.1, < 3.0.0) + emoji_regex (>= 0.1, < 2.0) + excon (>= 0.45.0, < 1.0.0) + faraday (~> 0.9) + faraday-cookie_jar (~> 0.0.6) + faraday_middleware (~> 0.9) + fastimage (>= 2.1.0, < 3.0.0) + gh_inspector (>= 1.1.2, < 2.0.0) + google-api-client (>= 0.21.2, < 0.24.0) + google-cloud-storage (>= 1.15.0, < 2.0.0) + highline (>= 1.7.2, < 2.0.0) + json (< 3.0.0) + jwt (~> 2.1.0) + mini_magick (>= 4.9.4, < 5.0.0) + multi_xml (~> 0.5) + multipart-post (~> 2.0.0) + plist (>= 3.1.0, < 4.0.0) + public_suffix (~> 2.0.0) + rubyzip (>= 1.2.2, < 2.0.0) + security (= 0.1.3) + simctl (~> 1.6.3) + slack-notifier (>= 2.0.0, < 3.0.0) + terminal-notifier (>= 2.0.0, < 3.0.0) + terminal-table (>= 1.4.5, < 2.0.0) + tty-screen (>= 0.6.3, < 1.0.0) + tty-spinner (>= 0.8.0, < 1.0.0) + word_wrap (~> 1.0.0) + xcodeproj (>= 1.8.1, < 2.0.0) + xcpretty (~> 0.3.0) + xcpretty-travis-formatter (>= 0.0.3) + gh_inspector (1.1.3) + google-api-client (0.23.9) + addressable (~> 2.5, >= 2.5.1) + googleauth (>= 0.5, < 0.7.0) + httpclient (>= 2.8.1, < 3.0) + mime-types (~> 3.0) + representable (~> 3.0) + retriable (>= 2.0, < 4.0) + signet (~> 0.9) + google-cloud-core (1.3.0) + google-cloud-env (~> 1.0) + google-cloud-env (1.2.0) + faraday (~> 0.11) + google-cloud-storage (1.16.0) + digest-crc (~> 0.4) + google-api-client (~> 0.23) + google-cloud-core (~> 1.2) + googleauth (>= 0.6.2, < 0.10.0) + googleauth (0.6.7) + faraday (~> 0.12) + jwt (>= 1.4, < 3.0) + memoist (~> 0.16) + multi_json (~> 1.11) + os (>= 0.9, < 2.0) + signet (~> 0.7) + highline (1.7.10) + http-cookie (1.0.3) + domain_name (~> 0.5) + httpclient (2.8.3) + json (2.2.0) + jwt (2.1.0) + memoist (0.16.0) + mime-types (3.2.2) + mime-types-data (~> 3.2015) + mime-types-data (3.2019.0331) + mini_magick (4.9.5) + multi_json (1.13.1) + multi_xml (0.6.0) + multipart-post (2.0.0) + nanaimo (0.2.6) + naturally (2.2.0) + os (1.0.1) + plist (3.5.0) + public_suffix (2.0.5) + representable (3.0.4) + declarative (< 0.1.0) + declarative-option (< 0.2.0) + uber (< 0.2.0) + retriable (3.1.2) + rouge (2.0.7) + rubyzip (1.2.3) + security (0.1.3) + signet (0.11.0) + addressable (~> 2.3) + faraday (~> 0.9) + jwt (>= 1.5, < 3.0) + multi_json (~> 1.10) + simctl (1.6.5) + CFPropertyList + naturally + slack-notifier (2.3.2) + terminal-notifier (2.0.0) + terminal-table (1.8.0) + unicode-display_width (~> 1.1, >= 1.1.1) + tty-cursor (0.7.0) + tty-screen (0.7.0) + tty-spinner (0.9.1) + tty-cursor (~> 0.7) + uber (0.1.0) + unf (0.1.4) + unf_ext + unf_ext (0.0.7.6) + unicode-display_width (1.6.0) + word_wrap (1.0.0) + xcodeproj (1.12.0) + CFPropertyList (>= 2.3.3, < 4.0) + atomos (~> 0.1.3) + claide (>= 1.0.2, < 2.0) + colored2 (~> 3.1) + nanaimo (~> 0.2.6) + xcpretty (0.3.0) + rouge (~> 2.0.7) + xcpretty-travis-formatter (1.0.0) + xcpretty (~> 0.2, >= 0.0.7) + +PLATFORMS + ruby + +DEPENDENCIES + fastlane + +BUNDLED WITH + 1.17.3 diff --git a/Go-Jek/GO-JEK-Assignment/Podfile b/Go-Jek/GO-JEK-Assignment/Podfile new file mode 100755 index 0000000..e217b37 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Podfile @@ -0,0 +1,17 @@ +platform :ios, '11.0' + +target 'GJAssignment' do + use_frameworks! + + pod 'MBProgressHUD', '~> 1.1.0' + + target 'GJAssignmentTests' do + inherit! :search_paths + # Pods for testing + end + +end + +target 'GJAssignmentUITests' do + inherit! :search_paths +end diff --git a/Go-Jek/GO-JEK-Assignment/Podfile.lock b/Go-Jek/GO-JEK-Assignment/Podfile.lock new file mode 100755 index 0000000..2641023 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Podfile.lock @@ -0,0 +1,16 @@ +PODS: + - MBProgressHUD (1.1.0) + +DEPENDENCIES: + - MBProgressHUD (~> 1.1.0) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - MBProgressHUD + +SPEC CHECKSUMS: + MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 + +PODFILE CHECKSUM: ace7c7ec8272833eaeda20e1d138b7206d558901 + +COCOAPODS: 1.7.5 diff --git a/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/LICENSE b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/LICENSE new file mode 100755 index 0000000..1c0d59b --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/LICENSE @@ -0,0 +1,19 @@ +Copyright © 2009-2016 Matej Bukovinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.h b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.h new file mode 100755 index 0000000..a7b54ee --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.h @@ -0,0 +1,443 @@ +// +// MBProgressHUD.h +// Version 1.1.0 +// Created by Matej Bukovinski on 2.4.09. +// + +// This code is distributed under the terms and conditions of the MIT license. + +// Copyright © 2009-2016 Matej Bukovinski +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. + +#import +#import +#import + +@class MBBackgroundView; +@protocol MBProgressHUDDelegate; + + +extern CGFloat const MBProgressMaxOffset; + +typedef NS_ENUM(NSInteger, MBProgressHUDMode) { + /// UIActivityIndicatorView. + MBProgressHUDModeIndeterminate, + /// A round, pie-chart like, progress view. + MBProgressHUDModeDeterminate, + /// Horizontal progress bar. + MBProgressHUDModeDeterminateHorizontalBar, + /// Ring-shaped progress view. + MBProgressHUDModeAnnularDeterminate, + /// Shows a custom view. + MBProgressHUDModeCustomView, + /// Shows only labels. + MBProgressHUDModeText +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDAnimation) { + /// Opacity animation + MBProgressHUDAnimationFade, + /// Opacity + scale animation (zoom in when appearing zoom out when disappearing) + MBProgressHUDAnimationZoom, + /// Opacity + scale animation (zoom out style) + MBProgressHUDAnimationZoomOut, + /// Opacity + scale animation (zoom in style) + MBProgressHUDAnimationZoomIn +}; + +typedef NS_ENUM(NSInteger, MBProgressHUDBackgroundStyle) { + /// Solid color background + MBProgressHUDBackgroundStyleSolidColor, + /// UIVisualEffectView or UIToolbar.layer background view + MBProgressHUDBackgroundStyleBlur +}; + +typedef void (^MBProgressHUDCompletionBlock)(void); + + +NS_ASSUME_NONNULL_BEGIN + + +/** + * Displays a simple HUD window containing a progress indicator and two optional labels for short messages. + * + * This is a simple drop-in class for displaying a progress HUD view similar to Apple's private UIProgressHUD class. + * The MBProgressHUD window spans over the entire space given to it by the initWithFrame: constructor and catches all + * user input on this region, thereby preventing the user operations on components below the view. + * + * @note To still allow touches to pass through the HUD, you can set hud.userInteractionEnabled = NO. + * @attention MBProgressHUD is a UI class and should therefore only be accessed on the main thread. + */ +@interface MBProgressHUD : UIView + +/** + * Creates a new HUD, adds it to provided view and shows it. The counterpart to this method is hideHUDForView:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that the HUD will be added to + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * @return A reference to the created HUD. + * + * @see hideHUDForView:animated: + * @see animationType + */ ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated; + +/// @name Showing and hiding + +/** + * Finds the top-most HUD subview that hasn't finished and hides it. The counterpart to this method is showHUDAddedTo:animated:. + * + * @note This method sets removeFromSuperViewOnHide. The HUD will automatically be removed from the view hierarchy when hidden. + * + * @param view The view that is going to be searched for a HUD subview. + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @return YES if a HUD was found and removed, NO otherwise. + * + * @see showHUDAddedTo:animated: + * @see animationType + */ ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated; + +/** + * Finds the top-most HUD subview that hasn't finished and returns it. + * + * @param view The view that is going to be searched. + * @return A reference to the last HUD subview discovered. + */ ++ (nullable MBProgressHUD *)HUDForView:(UIView *)view; + +/** + * A convenience constructor that initializes the HUD with the view's bounds. Calls the designated constructor with + * view.bounds as the parameter. + * + * @param view The view instance that will provide the bounds for the HUD. Should be the same instance as + * the HUD's superview (i.e., the view that the HUD will be added to). + */ +- (instancetype)initWithView:(UIView *)view; + +/** + * Displays the HUD. + * + * @note You need to make sure that the main thread completes its run loop soon after this method call so that + * the user interface can be updated. Call this method when your task is already set up to be executed in a new thread + * (e.g., when using something like NSOperation or making an asynchronous call like NSURLRequest). + * + * @param animated If set to YES the HUD will appear using the current animationType. If set to NO the HUD will not use + * animations while appearing. + * + * @see animationType + */ +- (void)showAnimated:(BOOL)animated; + +/** + * Hides the HUD. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated; + +/** + * Hides the HUD after a delay. This still calls the hudWasHidden: delegate. This is the counterpart of the show: method. Use it to + * hide the HUD when your task completes. + * + * @param animated If set to YES the HUD will disappear using the current animationType. If set to NO the HUD will not use + * animations while disappearing. + * @param delay Delay in seconds until the HUD is hidden. + * + * @see animationType + */ +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay; + +/** + * The HUD delegate object. Receives HUD state notifications. + */ +@property (weak, nonatomic) id delegate; + +/** + * Called after the HUD is hiden. + */ +@property (copy, nullable) MBProgressHUDCompletionBlock completionBlock; + +/* + * Grace period is the time (in seconds) that the invoked method may be run without + * showing the HUD. If the task finishes before the grace time runs out, the HUD will + * not be shown at all. + * This may be used to prevent HUD display for very short tasks. + * Defaults to 0 (no grace time). + */ +@property (assign, nonatomic) NSTimeInterval graceTime; + +/** + * The minimum time (in seconds) that the HUD is shown. + * This avoids the problem of the HUD being shown and than instantly hidden. + * Defaults to 0 (no minimum show time). + */ +@property (assign, nonatomic) NSTimeInterval minShowTime; + +/** + * Removes the HUD from its parent view when hidden. + * Defaults to NO. + */ +@property (assign, nonatomic) BOOL removeFromSuperViewOnHide; + +/// @name Appearance + +/** + * MBProgressHUD operation mode. The default is MBProgressHUDModeIndeterminate. + */ +@property (assign, nonatomic) MBProgressHUDMode mode; + +/** + * A color that gets forwarded to all labels and supported indicators. Also sets the tintColor + * for custom views on iOS 7+. Set to nil to manage color individually. + * Defaults to semi-translucent black on iOS 7 and later and white on earlier iOS versions. + */ +@property (strong, nonatomic, nullable) UIColor *contentColor UI_APPEARANCE_SELECTOR; + +/** + * The animation type that should be used when the HUD is shown and hidden. + */ +@property (assign, nonatomic) MBProgressHUDAnimation animationType UI_APPEARANCE_SELECTOR; + +/** + * The bezel offset relative to the center of the view. You can use MBProgressMaxOffset + * and -MBProgressMaxOffset to move the HUD all the way to the screen edge in each direction. + * E.g., CGPointMake(0.f, MBProgressMaxOffset) would position the HUD centered on the bottom edge. + */ +@property (assign, nonatomic) CGPoint offset UI_APPEARANCE_SELECTOR; + +/** + * The amount of space between the HUD edge and the HUD elements (labels, indicators or custom views). + * This also represents the minimum bezel distance to the edge of the HUD view. + * Defaults to 20.f + */ +@property (assign, nonatomic) CGFloat margin UI_APPEARANCE_SELECTOR; + +/** + * The minimum size of the HUD bezel. Defaults to CGSizeZero (no minimum size). + */ +@property (assign, nonatomic) CGSize minSize UI_APPEARANCE_SELECTOR; + +/** + * Force the HUD dimensions to be equal if possible. + */ +@property (assign, nonatomic, getter = isSquare) BOOL square UI_APPEARANCE_SELECTOR; + +/** + * When enabled, the bezel center gets slightly affected by the device accelerometer data. + * Has no effect on iOS < 7.0. Defaults to YES. + */ +@property (assign, nonatomic, getter=areDefaultMotionEffectsEnabled) BOOL defaultMotionEffectsEnabled UI_APPEARANCE_SELECTOR; + +/// @name Progress + +/** + * The progress of the progress indicator, from 0.0 to 1.0. Defaults to 0.0. + */ +@property (assign, nonatomic) float progress; + +/// @name ProgressObject + +/** + * The NSProgress object feeding the progress information to the progress indicator. + */ +@property (strong, nonatomic, nullable) NSProgress *progressObject; + +/// @name Views + +/** + * The view containing the labels and indicator (or customView). + */ +@property (strong, nonatomic, readonly) MBBackgroundView *bezelView; + +/** + * View covering the entire HUD area, placed behind bezelView. + */ +@property (strong, nonatomic, readonly) MBBackgroundView *backgroundView; + +/** + * The UIView (e.g., a UIImageView) to be shown when the HUD is in MBProgressHUDModeCustomView. + * The view should implement intrinsicContentSize for proper sizing. For best results use approximately 37 by 37 pixels. + */ +@property (strong, nonatomic, nullable) UIView *customView; + +/** + * A label that holds an optional short message to be displayed below the activity indicator. The HUD is automatically resized to fit + * the entire text. + */ +@property (strong, nonatomic, readonly) UILabel *label; + +/** + * A label that holds an optional details message displayed below the labelText message. The details text can span multiple lines. + */ +@property (strong, nonatomic, readonly) UILabel *detailsLabel; + +/** + * A button that is placed below the labels. Visible only if a target / action is added. + */ +@property (strong, nonatomic, readonly) UIButton *button; + +@end + + +@protocol MBProgressHUDDelegate + +@optional + +/** + * Called after the HUD was fully hidden from the screen. + */ +- (void)hudWasHidden:(MBProgressHUD *)hud; + +@end + + +/** + * A progress view for showing definite progress by filling up a circle (pie chart). + */ +@interface MBRoundProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Indicator progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressTintColor; + +/** + * Indicator background (non-progress) color. + * Only applicable on iOS versions older than iOS 7. + * Defaults to translucent white (alpha 0.1). + */ +@property (nonatomic, strong) UIColor *backgroundTintColor; + +/* + * Display mode - NO = round or YES = annular. Defaults to round. + */ +@property (nonatomic, assign, getter = isAnnular) BOOL annular; + +@end + + +/** + * A flat bar progress view. + */ +@interface MBBarProgressView : UIView + +/** + * Progress (0.0 to 1.0) + */ +@property (nonatomic, assign) float progress; + +/** + * Bar border line color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *lineColor; + +/** + * Bar background color. + * Defaults to clear [UIColor clearColor]; + */ +@property (nonatomic, strong) UIColor *progressRemainingColor; + +/** + * Bar progress color. + * Defaults to white [UIColor whiteColor]. + */ +@property (nonatomic, strong) UIColor *progressColor; + +@end + + +@interface MBBackgroundView : UIView + +/** + * The background style. + * Defaults to MBProgressHUDBackgroundStyleBlur on iOS 7 or later and MBProgressHUDBackgroundStyleSolidColor otherwise. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic) MBProgressHUDBackgroundStyle style; + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV +/** + * The blur effect style, when using MBProgressHUDBackgroundStyleBlur. + * Defaults to UIBlurEffectStyleLight. + */ +@property (nonatomic) UIBlurEffectStyle blurEffectStyle; +#endif + +/** + * The background color or the blur tint color. + * @note Due to iOS 7 not supporting UIVisualEffectView, the blur effect differs slightly between iOS 7 and later versions. + */ +@property (nonatomic, strong) UIColor *color; + +@end + +@interface MBProgressHUD (Deprecated) + ++ (NSArray *)allHUDsForView:(UIView *)view __attribute__((deprecated("Store references when using more than one HUD per view."))); ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated __attribute__((deprecated("Store references when using more than one HUD per view."))); + +- (id)initWithWindow:(UIWindow *)window __attribute__((deprecated("Use initWithView: instead."))); + +- (void)show:(BOOL)animated __attribute__((deprecated("Use showAnimated: instead."))); +- (void)hide:(BOOL)animated __attribute__((deprecated("Use hideAnimated: instead."))); +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay __attribute__((deprecated("Use hideAnimated:afterDelay: instead."))); + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue __attribute__((deprecated("Use GCD directly."))); +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue + completionBlock:(nullable MBProgressHUDCompletionBlock)completion __attribute__((deprecated("Use GCD directly."))); +@property (assign) BOOL taskInProgress __attribute__((deprecated("No longer needed."))); + +@property (nonatomic, copy) NSString *labelText __attribute__((deprecated("Use label.text instead."))); +@property (nonatomic, strong) UIFont *labelFont __attribute__((deprecated("Use label.font instead."))); +@property (nonatomic, strong) UIColor *labelColor __attribute__((deprecated("Use label.textColor instead."))); +@property (nonatomic, copy) NSString *detailsLabelText __attribute__((deprecated("Use detailsLabel.text instead."))); +@property (nonatomic, strong) UIFont *detailsLabelFont __attribute__((deprecated("Use detailsLabel.font instead."))); +@property (nonatomic, strong) UIColor *detailsLabelColor __attribute__((deprecated("Use detailsLabel.textColor instead."))); +@property (assign, nonatomic) CGFloat opacity __attribute__((deprecated("Customize bezelView properties instead."))); +@property (strong, nonatomic) UIColor *color __attribute__((deprecated("Customize the bezelView color instead."))); +@property (assign, nonatomic) CGFloat xOffset __attribute__((deprecated("Set offset.x instead."))); +@property (assign, nonatomic) CGFloat yOffset __attribute__((deprecated("Set offset.y instead."))); +@property (assign, nonatomic) CGFloat cornerRadius __attribute__((deprecated("Set bezelView.layer.cornerRadius instead."))); +@property (assign, nonatomic) BOOL dimBackground __attribute__((deprecated("Customize HUD background properties instead."))); +@property (strong, nonatomic) UIColor *activityIndicatorColor __attribute__((deprecated("Use UIAppearance to customize UIActivityIndicatorView. E.g.: [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil].color = [UIColor redColor];"))); +@property (atomic, assign, readonly) CGSize size __attribute__((deprecated("Get the bezelView.frame.size instead."))); + +@end + +NS_ASSUME_NONNULL_END diff --git a/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.m b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.m new file mode 100755 index 0000000..1bcf9e9 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/MBProgressHUD.m @@ -0,0 +1,1495 @@ +// +// MBProgressHUD.m +// Version 1.1.0 +// Created by Matej Bukovinski on 2.4.09. +// + +#import "MBProgressHUD.h" +#import + + +#ifndef kCFCoreFoundationVersionNumber_iOS_7_0 + #define kCFCoreFoundationVersionNumber_iOS_7_0 847.20 +#endif + +#ifndef kCFCoreFoundationVersionNumber_iOS_8_0 + #define kCFCoreFoundationVersionNumber_iOS_8_0 1129.15 +#endif + +#define MBMainThreadAssert() NSAssert([NSThread isMainThread], @"MBProgressHUD needs to be accessed on the main thread."); + +CGFloat const MBProgressMaxOffset = 1000000.f; + +static const CGFloat MBDefaultPadding = 4.f; +static const CGFloat MBDefaultLabelFontSize = 16.f; +static const CGFloat MBDefaultDetailsLabelFontSize = 12.f; + + +@interface MBProgressHUD () { + // Deprecated + UIColor *_activityIndicatorColor; + CGFloat _opacity; +} + +@property (nonatomic, assign) BOOL useAnimation; +@property (nonatomic, assign, getter=hasFinished) BOOL finished; +@property (nonatomic, strong) UIView *indicator; +@property (nonatomic, strong) NSDate *showStarted; +@property (nonatomic, strong) NSArray *paddingConstraints; +@property (nonatomic, strong) NSArray *bezelConstraints; +@property (nonatomic, strong) UIView *topSpacer; +@property (nonatomic, strong) UIView *bottomSpacer; +@property (nonatomic, weak) NSTimer *graceTimer; +@property (nonatomic, weak) NSTimer *minShowTimer; +@property (nonatomic, weak) NSTimer *hideDelayTimer; +@property (nonatomic, weak) CADisplayLink *progressObjectDisplayLink; + +// Deprecated +@property (assign) BOOL taskInProgress; + +@end + + +@interface MBProgressHUDRoundedButton : UIButton +@end + + +@implementation MBProgressHUD + +#pragma mark - Class methods + ++ (instancetype)showHUDAddedTo:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [[self alloc] initWithView:view]; + hud.removeFromSuperViewOnHide = YES; + [view addSubview:hud]; + [hud showAnimated:animated]; + return hud; +} + ++ (BOOL)hideHUDForView:(UIView *)view animated:(BOOL)animated { + MBProgressHUD *hud = [self HUDForView:view]; + if (hud != nil) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + return YES; + } + return NO; +} + ++ (MBProgressHUD *)HUDForView:(UIView *)view { + NSEnumerator *subviewsEnum = [view.subviews reverseObjectEnumerator]; + for (UIView *subview in subviewsEnum) { + if ([subview isKindOfClass:self]) { + MBProgressHUD *hud = (MBProgressHUD *)subview; + if (hud.hasFinished == NO) { + return hud; + } + } + } + return nil; +} + +#pragma mark - Lifecycle + +- (void)commonInit { + // Set default values for properties + _animationType = MBProgressHUDAnimationFade; + _mode = MBProgressHUDModeIndeterminate; + _margin = 20.0f; + _opacity = 1.f; + _defaultMotionEffectsEnabled = YES; + + // Default color, depending on the current iOS version + BOOL isLegacy = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + _contentColor = isLegacy ? [UIColor whiteColor] : [UIColor colorWithWhite:0.f alpha:0.7f]; + // Transparent background + self.opaque = NO; + self.backgroundColor = [UIColor clearColor]; + // Make it invisible for now + self.alpha = 0.0f; + self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + self.layer.allowsGroupOpacity = NO; + + [self setupViews]; + [self updateIndicators]; + [self registerForNotifications]; +} + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + [self commonInit]; + } + return self; +} + +- (instancetype)initWithCoder:(NSCoder *)aDecoder { + if ((self = [super initWithCoder:aDecoder])) { + [self commonInit]; + } + return self; +} + +- (id)initWithView:(UIView *)view { + NSAssert(view, @"View must not be nil."); + return [self initWithFrame:view.bounds]; +} + +- (void)dealloc { + [self unregisterFromNotifications]; +} + +#pragma mark - Show & hide + +- (void)showAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.minShowTimer invalidate]; + self.useAnimation = animated; + self.finished = NO; + // If the grace time is set, postpone the HUD display + if (self.graceTime > 0.0) { + NSTimer *timer = [NSTimer timerWithTimeInterval:self.graceTime target:self selector:@selector(handleGraceTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.graceTimer = timer; + } + // ... otherwise show the HUD immediately + else { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)hideAnimated:(BOOL)animated { + MBMainThreadAssert(); + [self.graceTimer invalidate]; + self.useAnimation = animated; + self.finished = YES; + // If the minShow time is set, calculate how long the HUD was shown, + // and postpone the hiding operation if necessary + if (self.minShowTime > 0.0 && self.showStarted) { + NSTimeInterval interv = [[NSDate date] timeIntervalSinceDate:self.showStarted]; + if (interv < self.minShowTime) { + NSTimer *timer = [NSTimer timerWithTimeInterval:(self.minShowTime - interv) target:self selector:@selector(handleMinShowTimer:) userInfo:nil repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.minShowTimer = timer; + return; + } + } + // ... otherwise hide the HUD immediately + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)hideAnimated:(BOOL)animated afterDelay:(NSTimeInterval)delay { + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + + NSTimer *timer = [NSTimer timerWithTimeInterval:delay target:self selector:@selector(handleHideTimer:) userInfo:@(animated) repeats:NO]; + [[NSRunLoop currentRunLoop] addTimer:timer forMode:NSRunLoopCommonModes]; + self.hideDelayTimer = timer; +} + +#pragma mark - Timer callbacks + +- (void)handleGraceTimer:(NSTimer *)theTimer { + // Show the HUD only if the task is still running + if (!self.hasFinished) { + [self showUsingAnimation:self.useAnimation]; + } +} + +- (void)handleMinShowTimer:(NSTimer *)theTimer { + [self hideUsingAnimation:self.useAnimation]; +} + +- (void)handleHideTimer:(NSTimer *)timer { + [self hideAnimated:[timer.userInfo boolValue]]; +} + +#pragma mark - View Hierrarchy + +- (void)didMoveToSuperview { + [self updateForCurrentOrientationAnimated:NO]; +} + +#pragma mark - Internal show & hide operations + +- (void)showUsingAnimation:(BOOL)animated { + // Cancel any previous animations + [self.bezelView.layer removeAllAnimations]; + [self.backgroundView.layer removeAllAnimations]; + + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + + self.showStarted = [NSDate date]; + self.alpha = 1.f; + + // Needed in case we hide and re-show with the same NSProgress object attached. + [self setNSProgressDisplayLinkEnabled:YES]; + + if (animated) { + [self animateIn:YES withType:self.animationType completion:NULL]; + } else { +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + self.bezelView.alpha = self.opacity; +#pragma clang diagnostic pop + self.backgroundView.alpha = 1.f; + } +} + +- (void)hideUsingAnimation:(BOOL)animated { + if (animated && self.showStarted) { + self.showStarted = nil; + [self animateIn:NO withType:self.animationType completion:^(BOOL finished) { + [self done]; + }]; + } else { + self.showStarted = nil; + self.bezelView.alpha = 0.f; + self.backgroundView.alpha = 1.f; + [self done]; + } +} + +- (void)animateIn:(BOOL)animatingIn withType:(MBProgressHUDAnimation)type completion:(void(^)(BOOL finished))completion { + // Automatically determine the correct zoom animation type + if (type == MBProgressHUDAnimationZoom) { + type = animatingIn ? MBProgressHUDAnimationZoomIn : MBProgressHUDAnimationZoomOut; + } + + CGAffineTransform small = CGAffineTransformMakeScale(0.5f, 0.5f); + CGAffineTransform large = CGAffineTransformMakeScale(1.5f, 1.5f); + + // Set starting state + UIView *bezelView = self.bezelView; + if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = small; + } else if (animatingIn && bezelView.alpha == 0.f && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = large; + } + + // Perform animations + dispatch_block_t animations = ^{ + if (animatingIn) { + bezelView.transform = CGAffineTransformIdentity; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomIn) { + bezelView.transform = large; + } else if (!animatingIn && type == MBProgressHUDAnimationZoomOut) { + bezelView.transform = small; + } +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + bezelView.alpha = animatingIn ? self.opacity : 0.f; +#pragma clang diagnostic pop + self.backgroundView.alpha = animatingIn ? 1.f : 0.f; + }; + + // Spring animations are nicer, but only available on iOS 7+ +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + [UIView animateWithDuration:0.3 delay:0. usingSpringWithDamping:1.f initialSpringVelocity:0.f options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; + return; + } +#endif + [UIView animateWithDuration:0.3 delay:0. options:UIViewAnimationOptionBeginFromCurrentState animations:animations completion:completion]; +} + +- (void)done { + // Cancel any scheduled hideDelayed: calls + [self.hideDelayTimer invalidate]; + [self setNSProgressDisplayLinkEnabled:NO]; + + if (self.hasFinished) { + self.alpha = 0.0f; + if (self.removeFromSuperViewOnHide) { + [self removeFromSuperview]; + } + } + MBProgressHUDCompletionBlock completionBlock = self.completionBlock; + if (completionBlock) { + completionBlock(); + } + id delegate = self.delegate; + if ([delegate respondsToSelector:@selector(hudWasHidden:)]) { + [delegate performSelector:@selector(hudWasHidden:) withObject:self]; + } +} + +#pragma mark - UI + +- (void)setupViews { + UIColor *defaultColor = self.contentColor; + + MBBackgroundView *backgroundView = [[MBBackgroundView alloc] initWithFrame:self.bounds]; + backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + backgroundView.backgroundColor = [UIColor clearColor]; + backgroundView.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight; + backgroundView.alpha = 0.f; + [self addSubview:backgroundView]; + _backgroundView = backgroundView; + + MBBackgroundView *bezelView = [MBBackgroundView new]; + bezelView.translatesAutoresizingMaskIntoConstraints = NO; + bezelView.layer.cornerRadius = 5.f; + bezelView.alpha = 0.f; + [self addSubview:bezelView]; + _bezelView = bezelView; + [self updateBezelMotionEffects]; + + UILabel *label = [UILabel new]; + label.adjustsFontSizeToFitWidth = NO; + label.textAlignment = NSTextAlignmentCenter; + label.textColor = defaultColor; + label.font = [UIFont boldSystemFontOfSize:MBDefaultLabelFontSize]; + label.opaque = NO; + label.backgroundColor = [UIColor clearColor]; + _label = label; + + UILabel *detailsLabel = [UILabel new]; + detailsLabel.adjustsFontSizeToFitWidth = NO; + detailsLabel.textAlignment = NSTextAlignmentCenter; + detailsLabel.textColor = defaultColor; + detailsLabel.numberOfLines = 0; + detailsLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + detailsLabel.opaque = NO; + detailsLabel.backgroundColor = [UIColor clearColor]; + _detailsLabel = detailsLabel; + + UIButton *button = [MBProgressHUDRoundedButton buttonWithType:UIButtonTypeCustom]; + button.titleLabel.textAlignment = NSTextAlignmentCenter; + button.titleLabel.font = [UIFont boldSystemFontOfSize:MBDefaultDetailsLabelFontSize]; + [button setTitleColor:defaultColor forState:UIControlStateNormal]; + _button = button; + + for (UIView *view in @[label, detailsLabel, button]) { + view.translatesAutoresizingMaskIntoConstraints = NO; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [view setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + [bezelView addSubview:view]; + } + + UIView *topSpacer = [UIView new]; + topSpacer.translatesAutoresizingMaskIntoConstraints = NO; + topSpacer.hidden = YES; + [bezelView addSubview:topSpacer]; + _topSpacer = topSpacer; + + UIView *bottomSpacer = [UIView new]; + bottomSpacer.translatesAutoresizingMaskIntoConstraints = NO; + bottomSpacer.hidden = YES; + [bezelView addSubview:bottomSpacer]; + _bottomSpacer = bottomSpacer; +} + +- (void)updateIndicators { + UIView *indicator = self.indicator; + BOOL isActivityIndicator = [indicator isKindOfClass:[UIActivityIndicatorView class]]; + BOOL isRoundIndicator = [indicator isKindOfClass:[MBRoundProgressView class]]; + + MBProgressHUDMode mode = self.mode; + if (mode == MBProgressHUDModeIndeterminate) { + if (!isActivityIndicator) { + // Update to indeterminate indicator + [indicator removeFromSuperview]; + indicator = [[UIActivityIndicatorView alloc] initWithActivityIndicatorStyle:UIActivityIndicatorViewStyleWhiteLarge]; + [(UIActivityIndicatorView *)indicator startAnimating]; + [self.bezelView addSubview:indicator]; + } + } + else if (mode == MBProgressHUDModeDeterminateHorizontalBar) { + // Update to bar determinate indicator + [indicator removeFromSuperview]; + indicator = [[MBBarProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeDeterminate || mode == MBProgressHUDModeAnnularDeterminate) { + if (!isRoundIndicator) { + // Update to determinante indicator + [indicator removeFromSuperview]; + indicator = [[MBRoundProgressView alloc] init]; + [self.bezelView addSubview:indicator]; + } + if (mode == MBProgressHUDModeAnnularDeterminate) { + [(MBRoundProgressView *)indicator setAnnular:YES]; + } + } + else if (mode == MBProgressHUDModeCustomView && self.customView != indicator) { + // Update custom view indicator + [indicator removeFromSuperview]; + indicator = self.customView; + [self.bezelView addSubview:indicator]; + } + else if (mode == MBProgressHUDModeText) { + [indicator removeFromSuperview]; + indicator = nil; + } + indicator.translatesAutoresizingMaskIntoConstraints = NO; + self.indicator = indicator; + + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisHorizontal]; + [indicator setContentCompressionResistancePriority:998.f forAxis:UILayoutConstraintAxisVertical]; + + [self updateViewsForColor:self.contentColor]; + [self setNeedsUpdateConstraints]; +} + +- (void)updateViewsForColor:(UIColor *)color { + if (!color) return; + + self.label.textColor = color; + self.detailsLabel.textColor = color; + [self.button setTitleColor:color forState:UIControlStateNormal]; + +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Wdeprecated-declarations" + if (self.activityIndicatorColor) { + color = self.activityIndicatorColor; + } +#pragma clang diagnostic pop + + // UIAppearance settings are prioritized. If they are preset the set color is ignored. + + UIView *indicator = self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + UIActivityIndicatorView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [UIActivityIndicatorView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + // For iOS 9+ + appearance = [UIActivityIndicatorView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + + if (appearance.color == nil) { + ((UIActivityIndicatorView *)indicator).color = color; + } + } else if ([indicator isKindOfClass:[MBRoundProgressView class]]) { + MBRoundProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBRoundProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBRoundProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressTintColor == nil) { + ((MBRoundProgressView *)indicator).progressTintColor = color; + } + if (appearance.backgroundTintColor == nil) { + ((MBRoundProgressView *)indicator).backgroundTintColor = [color colorWithAlphaComponent:0.1]; + } + } else if ([indicator isKindOfClass:[MBBarProgressView class]]) { + MBBarProgressView *appearance = nil; +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 90000 + appearance = [MBBarProgressView appearanceWhenContainedIn:[MBProgressHUD class], nil]; +#else + appearance = [MBBarProgressView appearanceWhenContainedInInstancesOfClasses:@[[MBProgressHUD class]]]; +#endif + if (appearance.progressColor == nil) { + ((MBBarProgressView *)indicator).progressColor = color; + } + if (appearance.lineColor == nil) { + ((MBBarProgressView *)indicator).lineColor = color; + } + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + if ([indicator respondsToSelector:@selector(setTintColor:)]) { + [indicator setTintColor:color]; + } +#endif + } +} + +- (void)updateBezelMotionEffects { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 70000 || TARGET_OS_TV + MBBackgroundView *bezelView = self.bezelView; + if (![bezelView respondsToSelector:@selector(addMotionEffect:)]) return; + + if (self.defaultMotionEffectsEnabled) { + CGFloat effectOffset = 10.f; + UIInterpolatingMotionEffect *effectX = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.x" type:UIInterpolatingMotionEffectTypeTiltAlongHorizontalAxis]; + effectX.maximumRelativeValue = @(effectOffset); + effectX.minimumRelativeValue = @(-effectOffset); + + UIInterpolatingMotionEffect *effectY = [[UIInterpolatingMotionEffect alloc] initWithKeyPath:@"center.y" type:UIInterpolatingMotionEffectTypeTiltAlongVerticalAxis]; + effectY.maximumRelativeValue = @(effectOffset); + effectY.minimumRelativeValue = @(-effectOffset); + + UIMotionEffectGroup *group = [[UIMotionEffectGroup alloc] init]; + group.motionEffects = @[effectX, effectY]; + + [bezelView addMotionEffect:group]; + } else { + NSArray *effects = [bezelView motionEffects]; + for (UIMotionEffect *effect in effects) { + [bezelView removeMotionEffect:effect]; + } + } +#endif +} + +#pragma mark - Layout + +- (void)updateConstraints { + UIView *bezel = self.bezelView; + UIView *topSpacer = self.topSpacer; + UIView *bottomSpacer = self.bottomSpacer; + CGFloat margin = self.margin; + NSMutableArray *bezelConstraints = [NSMutableArray array]; + NSDictionary *metrics = @{@"margin": @(margin)}; + + NSMutableArray *subviews = [NSMutableArray arrayWithObjects:self.topSpacer, self.label, self.detailsLabel, self.button, self.bottomSpacer, nil]; + if (self.indicator) [subviews insertObject:self.indicator atIndex:1]; + + // Remove existing constraints + [self removeConstraints:self.constraints]; + [topSpacer removeConstraints:topSpacer.constraints]; + [bottomSpacer removeConstraints:bottomSpacer.constraints]; + if (self.bezelConstraints) { + [bezel removeConstraints:self.bezelConstraints]; + self.bezelConstraints = nil; + } + + // Center bezel in container (self), applying the offset if set + CGPoint offset = self.offset; + NSMutableArray *centeringConstraints = [NSMutableArray array]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterX multiplier:1.f constant:offset.x]]; + [centeringConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeCenterY relatedBy:NSLayoutRelationEqual toItem:self attribute:NSLayoutAttributeCenterY multiplier:1.f constant:offset.y]]; + [self applyPriority:998.f toConstraints:centeringConstraints]; + [self addConstraints:centeringConstraints]; + + // Ensure minimum side margin is kept + NSMutableArray *sideConstraints = [NSMutableArray array]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [sideConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"V:|-(>=margin)-[bezel]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(bezel)]]; + [self applyPriority:999.f toConstraints:sideConstraints]; + [self addConstraints:sideConstraints]; + + // Minimum bezel size, if set + CGSize minimumSize = self.minSize; + if (!CGSizeEqualToSize(minimumSize, CGSizeZero)) { + NSMutableArray *minSizeConstraints = [NSMutableArray array]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeWidth relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.width]]; + [minSizeConstraints addObject:[NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:minimumSize.height]]; + [self applyPriority:997.f toConstraints:minSizeConstraints]; + [bezelConstraints addObjectsFromArray:minSizeConstraints]; + } + + // Square aspect ratio, if set + if (self.square) { + NSLayoutConstraint *square = [NSLayoutConstraint constraintWithItem:bezel attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeWidth multiplier:1.f constant:0]; + square.priority = 997.f; + [bezelConstraints addObject:square]; + } + + // Top and bottom spacing + [topSpacer addConstraint:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + [bottomSpacer addConstraint:[NSLayoutConstraint constraintWithItem:bottomSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationGreaterThanOrEqual toItem:nil attribute:NSLayoutAttributeNotAnAttribute multiplier:1.f constant:margin]]; + // Top and bottom spaces should be equal + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:topSpacer attribute:NSLayoutAttributeHeight relatedBy:NSLayoutRelationEqual toItem:bottomSpacer attribute:NSLayoutAttributeHeight multiplier:1.f constant:0.f]]; + + // Layout subviews in bezel + NSMutableArray *paddingConstraints = [NSMutableArray new]; + [subviews enumerateObjectsUsingBlock:^(UIView *view, NSUInteger idx, BOOL *stop) { + // Center in bezel + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeCenterX relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeCenterX multiplier:1.f constant:0.f]]; + // Ensure the minimum edge margin is kept + [bezelConstraints addObjectsFromArray:[NSLayoutConstraint constraintsWithVisualFormat:@"|-(>=margin)-[view]-(>=margin)-|" options:0 metrics:metrics views:NSDictionaryOfVariableBindings(view)]]; + // Element spacing + if (idx == 0) { + // First, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeTop multiplier:1.f constant:0.f]]; + } else if (idx == subviews.count - 1) { + // Last, ensure spacing to bezel edge + [bezelConstraints addObject:[NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeBottom relatedBy:NSLayoutRelationEqual toItem:bezel attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]]; + } + if (idx > 0) { + // Has previous + NSLayoutConstraint *padding = [NSLayoutConstraint constraintWithItem:view attribute:NSLayoutAttributeTop relatedBy:NSLayoutRelationEqual toItem:subviews[idx - 1] attribute:NSLayoutAttributeBottom multiplier:1.f constant:0.f]; + [bezelConstraints addObject:padding]; + [paddingConstraints addObject:padding]; + } + }]; + + [bezel addConstraints:bezelConstraints]; + self.bezelConstraints = bezelConstraints; + + self.paddingConstraints = [paddingConstraints copy]; + [self updatePaddingConstraints]; + + [super updateConstraints]; +} + +- (void)layoutSubviews { + // There is no need to update constraints if they are going to + // be recreated in [super layoutSubviews] due to needsUpdateConstraints being set. + // This also avoids an issue on iOS 8, where updatePaddingConstraints + // would trigger a zombie object access. + if (!self.needsUpdateConstraints) { + [self updatePaddingConstraints]; + } + [super layoutSubviews]; +} + +- (void)updatePaddingConstraints { + // Set padding dynamically, depending on whether the view is visible or not + __block BOOL hasVisibleAncestors = NO; + [self.paddingConstraints enumerateObjectsUsingBlock:^(NSLayoutConstraint *padding, NSUInteger idx, BOOL *stop) { + UIView *firstView = (UIView *)padding.firstItem; + UIView *secondView = (UIView *)padding.secondItem; + BOOL firstVisible = !firstView.hidden && !CGSizeEqualToSize(firstView.intrinsicContentSize, CGSizeZero); + BOOL secondVisible = !secondView.hidden && !CGSizeEqualToSize(secondView.intrinsicContentSize, CGSizeZero); + // Set if both views are visible or if there's a visible view on top that doesn't have padding + // added relative to the current view yet + padding.constant = (firstVisible && (secondVisible || hasVisibleAncestors)) ? MBDefaultPadding : 0.f; + hasVisibleAncestors |= secondVisible; + }]; +} + +- (void)applyPriority:(UILayoutPriority)priority toConstraints:(NSArray *)constraints { + for (NSLayoutConstraint *constraint in constraints) { + constraint.priority = priority; + } +} + +#pragma mark - Properties + +- (void)setMode:(MBProgressHUDMode)mode { + if (mode != _mode) { + _mode = mode; + [self updateIndicators]; + } +} + +- (void)setCustomView:(UIView *)customView { + if (customView != _customView) { + _customView = customView; + if (self.mode == MBProgressHUDModeCustomView) { + [self updateIndicators]; + } + } +} + +- (void)setOffset:(CGPoint)offset { + if (!CGPointEqualToPoint(offset, _offset)) { + _offset = offset; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMargin:(CGFloat)margin { + if (margin != _margin) { + _margin = margin; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setMinSize:(CGSize)minSize { + if (!CGSizeEqualToSize(minSize, _minSize)) { + _minSize = minSize; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setSquare:(BOOL)square { + if (square != _square) { + _square = square; + [self setNeedsUpdateConstraints]; + } +} + +- (void)setProgressObjectDisplayLink:(CADisplayLink *)progressObjectDisplayLink { + if (progressObjectDisplayLink != _progressObjectDisplayLink) { + [_progressObjectDisplayLink invalidate]; + + _progressObjectDisplayLink = progressObjectDisplayLink; + + [_progressObjectDisplayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode]; + } +} + +- (void)setProgressObject:(NSProgress *)progressObject { + if (progressObject != _progressObject) { + _progressObject = progressObject; + [self setNSProgressDisplayLinkEnabled:YES]; + } +} + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + UIView *indicator = self.indicator; + if ([indicator respondsToSelector:@selector(setProgress:)]) { + [(id)indicator setValue:@(self.progress) forKey:@"progress"]; + } + } +} + +- (void)setContentColor:(UIColor *)contentColor { + if (contentColor != _contentColor && ![contentColor isEqual:_contentColor]) { + _contentColor = contentColor; + [self updateViewsForColor:contentColor]; + } +} + +- (void)setDefaultMotionEffectsEnabled:(BOOL)defaultMotionEffectsEnabled { + if (defaultMotionEffectsEnabled != _defaultMotionEffectsEnabled) { + _defaultMotionEffectsEnabled = defaultMotionEffectsEnabled; + [self updateBezelMotionEffects]; + } +} + +#pragma mark - NSProgress + +- (void)setNSProgressDisplayLinkEnabled:(BOOL)enabled { + // We're using CADisplayLink, because NSProgress can change very quickly and observing it may starve the main thread, + // so we're refreshing the progress only every frame draw + if (enabled && self.progressObject) { + // Only create if not already active. + if (!self.progressObjectDisplayLink) { + self.progressObjectDisplayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(updateProgressFromProgressObject)]; + } + } else { + self.progressObjectDisplayLink = nil; + } +} + +- (void)updateProgressFromProgressObject { + self.progress = self.progressObject.fractionCompleted; +} + +#pragma mark - Notifications + +- (void)registerForNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + + [nc addObserver:self selector:@selector(statusBarOrientationDidChange:) + name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +- (void)unregisterFromNotifications { +#if !TARGET_OS_TV + NSNotificationCenter *nc = [NSNotificationCenter defaultCenter]; + [nc removeObserver:self name:UIApplicationDidChangeStatusBarOrientationNotification object:nil]; +#endif +} + +#if !TARGET_OS_TV +- (void)statusBarOrientationDidChange:(NSNotification *)notification { + UIView *superview = self.superview; + if (!superview) { + return; + } else { + [self updateForCurrentOrientationAnimated:YES]; + } +} +#endif + +- (void)updateForCurrentOrientationAnimated:(BOOL)animated { + // Stay in sync with the superview in any case + if (self.superview) { + self.frame = self.superview.bounds; + } + + // Not needed on iOS 8+, compile out when the deployment target allows, + // to avoid sharedApplication problems on extension targets +#if __IPHONE_OS_VERSION_MIN_REQUIRED < 80000 + // Only needed pre iOS 8 when added to a window + BOOL iOS8OrLater = kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0; + if (iOS8OrLater || ![self.superview isKindOfClass:[UIWindow class]]) return; + + // Make extension friendly. Will not get called on extensions (iOS 8+) due to the above check. + // This just ensures we don't get a warning about extension-unsafe API. + Class UIApplicationClass = NSClassFromString(@"UIApplication"); + if (!UIApplicationClass || ![UIApplicationClass respondsToSelector:@selector(sharedApplication)]) return; + + UIApplication *application = [UIApplication performSelector:@selector(sharedApplication)]; + UIInterfaceOrientation orientation = application.statusBarOrientation; + CGFloat radians = 0; + + if (UIInterfaceOrientationIsLandscape(orientation)) { + radians = orientation == UIInterfaceOrientationLandscapeLeft ? -(CGFloat)M_PI_2 : (CGFloat)M_PI_2; + // Window coordinates differ! + self.bounds = CGRectMake(0, 0, self.bounds.size.height, self.bounds.size.width); + } else { + radians = orientation == UIInterfaceOrientationPortraitUpsideDown ? (CGFloat)M_PI : 0.f; + } + + if (animated) { + [UIView animateWithDuration:0.3 animations:^{ + self.transform = CGAffineTransformMakeRotation(radians); + }]; + } else { + self.transform = CGAffineTransformMakeRotation(radians); + } +#endif +} + +@end + + +@implementation MBRoundProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(0.f, 0.f, 37.f, 37.f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + _progress = 0.f; + _annular = NO; + _progressTintColor = [[UIColor alloc] initWithWhite:1.f alpha:1.f]; + _backgroundTintColor = [[UIColor alloc] initWithWhite:1.f alpha:.1f]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + return CGSizeMake(37.f, 37.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressTintColor:(UIColor *)progressTintColor { + NSAssert(progressTintColor, @"The color should not be nil."); + if (progressTintColor != _progressTintColor && ![progressTintColor isEqual:_progressTintColor]) { + _progressTintColor = progressTintColor; + [self setNeedsDisplay]; + } +} + +- (void)setBackgroundTintColor:(UIColor *)backgroundTintColor { + NSAssert(backgroundTintColor, @"The color should not be nil."); + if (backgroundTintColor != _backgroundTintColor && ![backgroundTintColor isEqual:_backgroundTintColor]) { + _backgroundTintColor = backgroundTintColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + + if (_annular) { + // Draw background + CGFloat lineWidth = isPreiOS7 ? 5.f : 2.f; + UIBezierPath *processBackgroundPath = [UIBezierPath bezierPath]; + processBackgroundPath.lineWidth = lineWidth; + processBackgroundPath.lineCapStyle = kCGLineCapButt; + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + CGFloat radius = (self.bounds.size.width - lineWidth)/2; + CGFloat startAngle = - ((float)M_PI / 2); // 90 degrees + CGFloat endAngle = (2 * (float)M_PI) + startAngle; + [processBackgroundPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_backgroundTintColor set]; + [processBackgroundPath stroke]; + // Draw progress + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = isPreiOS7 ? kCGLineCapRound : kCGLineCapSquare; + processPath.lineWidth = lineWidth; + endAngle = (self.progress * 2 * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + [_progressTintColor set]; + [processPath stroke]; + } else { + // Draw background + CGFloat lineWidth = 2.f; + CGRect allRect = self.bounds; + CGRect circleRect = CGRectInset(allRect, lineWidth/2.f, lineWidth/2.f); + CGPoint center = CGPointMake(CGRectGetMidX(self.bounds), CGRectGetMidY(self.bounds)); + [_progressTintColor setStroke]; + [_backgroundTintColor setFill]; + CGContextSetLineWidth(context, lineWidth); + if (isPreiOS7) { + CGContextFillEllipseInRect(context, circleRect); + } + CGContextStrokeEllipseInRect(context, circleRect); + // 90 degrees + CGFloat startAngle = - ((float)M_PI / 2.f); + // Draw progress + if (isPreiOS7) { + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - lineWidth; + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [_progressTintColor setFill]; + CGContextMoveToPoint(context, center.x, center.y); + CGContextAddArc(context, center.x, center.y, radius, startAngle, endAngle, 0); + CGContextClosePath(context); + CGContextFillPath(context); + } else { + UIBezierPath *processPath = [UIBezierPath bezierPath]; + processPath.lineCapStyle = kCGLineCapButt; + processPath.lineWidth = lineWidth * 2.f; + CGFloat radius = (CGRectGetWidth(self.bounds) / 2.f) - (processPath.lineWidth / 2.f); + CGFloat endAngle = (self.progress * 2.f * (float)M_PI) + startAngle; + [processPath addArcWithCenter:center radius:radius startAngle:startAngle endAngle:endAngle clockwise:YES]; + // Ensure that we don't get color overlapping when _progressTintColor alpha < 1.f. + CGContextSetBlendMode(context, kCGBlendModeCopy); + [_progressTintColor set]; + [processPath stroke]; + } + } +} + +@end + + +@implementation MBBarProgressView + +#pragma mark - Lifecycle + +- (id)init { + return [self initWithFrame:CGRectMake(.0f, .0f, 120.0f, 20.0f)]; +} + +- (id)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + _progress = 0.f; + _lineColor = [UIColor whiteColor]; + _progressColor = [UIColor whiteColor]; + _progressRemainingColor = [UIColor clearColor]; + self.backgroundColor = [UIColor clearColor]; + self.opaque = NO; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + BOOL isPreiOS7 = kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0; + return CGSizeMake(120.f, isPreiOS7 ? 20.f : 10.f); +} + +#pragma mark - Properties + +- (void)setProgress:(float)progress { + if (progress != _progress) { + _progress = progress; + [self setNeedsDisplay]; + } +} + +- (void)setProgressColor:(UIColor *)progressColor { + NSAssert(progressColor, @"The color should not be nil."); + if (progressColor != _progressColor && ![progressColor isEqual:_progressColor]) { + _progressColor = progressColor; + [self setNeedsDisplay]; + } +} + +- (void)setProgressRemainingColor:(UIColor *)progressRemainingColor { + NSAssert(progressRemainingColor, @"The color should not be nil."); + if (progressRemainingColor != _progressRemainingColor && ![progressRemainingColor isEqual:_progressRemainingColor]) { + _progressRemainingColor = progressRemainingColor; + [self setNeedsDisplay]; + } +} + +#pragma mark - Drawing + +- (void)drawRect:(CGRect)rect { + CGContextRef context = UIGraphicsGetCurrentContext(); + + CGContextSetLineWidth(context, 2); + CGContextSetStrokeColorWithColor(context,[_lineColor CGColor]); + CGContextSetFillColorWithColor(context, [_progressRemainingColor CGColor]); + + // Draw background and Border + CGFloat radius = (rect.size.height / 2) - 2; + CGContextMoveToPoint(context, 2, rect.size.height/2); + CGContextAddArcToPoint(context, 2, 2, radius + 2, 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, 2, rect.size.width - 2, rect.size.height / 2, radius); + CGContextAddArcToPoint(context, rect.size.width - 2, rect.size.height - 2, rect.size.width - radius - 2, rect.size.height - 2, radius); + CGContextAddArcToPoint(context, 2, rect.size.height - 2, 2, rect.size.height/2, radius); + CGContextDrawPath(context, kCGPathFillStroke); + + CGContextSetFillColorWithColor(context, [_progressColor CGColor]); + radius = radius - 2; + CGFloat amount = self.progress * rect.size.width; + + // Progress in the middle area + if (amount >= radius + 4 && amount <= (rect.size.width - radius - 4)) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, amount, 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, amount, rect.size.height - 4); + CGContextAddLineToPoint(context, amount, radius + 4); + + CGContextFillPath(context); + } + + // Progress in the right arc + else if (amount > radius + 4) { + CGFloat x = amount - (rect.size.width - radius - 4); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, 4); + CGFloat angle = -acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, M_PI, angle, 0); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, rect.size.width - radius - 4, rect.size.height - 4); + angle = acos(x/radius); + if (isnan(angle)) angle = 0; + CGContextAddArc(context, rect.size.width - radius - 4, rect.size.height/2, radius, -M_PI, angle, 1); + CGContextAddLineToPoint(context, amount, rect.size.height/2); + + CGContextFillPath(context); + } + + // Progress is in the left arc + else if (amount < radius + 4 && amount > 0) { + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, 4, radius + 4, 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextMoveToPoint(context, 4, rect.size.height/2); + CGContextAddArcToPoint(context, 4, rect.size.height - 4, radius + 4, rect.size.height - 4, radius); + CGContextAddLineToPoint(context, radius + 4, rect.size.height/2); + + CGContextFillPath(context); + } +} + +@end + + +@interface MBBackgroundView () + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV +@property UIVisualEffectView *effectView; +#endif +#if !TARGET_OS_TV +@property UIToolbar *toolbar; +#endif + +@end + + +@implementation MBBackgroundView + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + if ((self = [super initWithFrame:frame])) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_7_0) { + _style = MBProgressHUDBackgroundStyleBlur; +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + _blurEffectStyle = UIBlurEffectStyleLight; +#endif + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + _color = [UIColor colorWithWhite:0.8f alpha:0.6f]; + } else { + _color = [UIColor colorWithWhite:0.95f alpha:0.6f]; + } + } else { + _style = MBProgressHUDBackgroundStyleSolidColor; + _color = [[UIColor blackColor] colorWithAlphaComponent:0.8]; + } + + self.clipsToBounds = YES; + + [self updateForBackgroundStyle]; + } + return self; +} + +#pragma mark - Layout + +- (CGSize)intrinsicContentSize { + // Smallest size possible. Content pushes against this. + return CGSizeZero; +} + +#pragma mark - Appearance + +- (void)setStyle:(MBProgressHUDBackgroundStyle)style { + if (style == MBProgressHUDBackgroundStyleBlur && kCFCoreFoundationVersionNumber < kCFCoreFoundationVersionNumber_iOS_7_0) { + style = MBProgressHUDBackgroundStyleSolidColor; + } + if (_style != style) { + _style = style; + [self updateForBackgroundStyle]; + } +} + +- (void)setColor:(UIColor *)color { + NSAssert(color, @"The color should not be nil."); + if (color != _color && ![color isEqual:_color]) { + _color = color; + [self updateViewsForColor:color]; + } +} + +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + +- (void)setBlurEffectStyle:(UIBlurEffectStyle)blurEffectStyle { + if (_blurEffectStyle == blurEffectStyle) { + return; + } + + _blurEffectStyle = blurEffectStyle; + + [self updateForBackgroundStyle]; +} + +#endif + +/////////////////////////////////////////////////////////////////////////////////////////// +#pragma mark - Views + +- (void)updateForBackgroundStyle { + MBProgressHUDBackgroundStyle style = self.style; + if (style == MBProgressHUDBackgroundStyleBlur) { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + UIBlurEffect *effect = [UIBlurEffect effectWithStyle:self.blurEffectStyle]; + UIVisualEffectView *effectView = [[UIVisualEffectView alloc] initWithEffect:effect]; + [self addSubview:effectView]; + effectView.frame = self.bounds; + effectView.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + self.backgroundColor = self.color; + self.layer.allowsGroupOpacity = NO; + self.effectView = effectView; + } else { +#endif +#if !TARGET_OS_TV + UIToolbar *toolbar = [[UIToolbar alloc] initWithFrame:CGRectInset(self.bounds, -100.f, -100.f)]; + toolbar.autoresizingMask = UIViewAutoresizingFlexibleHeight | UIViewAutoresizingFlexibleWidth; + toolbar.barTintColor = self.color; + toolbar.translucent = YES; + [self addSubview:toolbar]; + self.toolbar = toolbar; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + } else { +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + [self.effectView removeFromSuperview]; + self.effectView = nil; + } else { +#endif +#if !TARGET_OS_TV + [self.toolbar removeFromSuperview]; + self.toolbar = nil; +#endif +#if __IPHONE_OS_VERSION_MAX_ALLOWED >= 80000 || TARGET_OS_TV + } +#endif + self.backgroundColor = self.color; + } +} + +- (void)updateViewsForColor:(UIColor *)color { + if (self.style == MBProgressHUDBackgroundStyleBlur) { + if (kCFCoreFoundationVersionNumber >= kCFCoreFoundationVersionNumber_iOS_8_0) { + self.backgroundColor = self.color; + } else { +#if !TARGET_OS_TV + self.toolbar.barTintColor = color; +#endif + } + } else { + self.backgroundColor = self.color; + } +} + +@end + + +@implementation MBProgressHUD (Deprecated) + +#pragma mark - Class + ++ (NSUInteger)hideAllHUDsForView:(UIView *)view animated:(BOOL)animated { + NSArray *huds = [MBProgressHUD allHUDsForView:view]; + for (MBProgressHUD *hud in huds) { + hud.removeFromSuperViewOnHide = YES; + [hud hideAnimated:animated]; + } + return [huds count]; +} + ++ (NSArray *)allHUDsForView:(UIView *)view { + NSMutableArray *huds = [NSMutableArray array]; + NSArray *subviews = view.subviews; + for (UIView *aView in subviews) { + if ([aView isKindOfClass:self]) { + [huds addObject:aView]; + } + } + return [NSArray arrayWithArray:huds]; +} + +#pragma mark - Lifecycle + +- (id)initWithWindow:(UIWindow *)window { + return [self initWithView:window]; +} + +#pragma mark - Show & hide + +- (void)show:(BOOL)animated { + [self showAnimated:animated]; +} + +- (void)hide:(BOOL)animated { + [self hideAnimated:animated]; +} + +- (void)hide:(BOOL)animated afterDelay:(NSTimeInterval)delay { + [self hideAnimated:animated afterDelay:delay]; +} + +#pragma mark - Threading + +- (void)showWhileExecuting:(SEL)method onTarget:(id)target withObject:(id)object animated:(BOOL)animated { + [self showAnimated:animated whileExecutingBlock:^{ +#pragma clang diagnostic push +#pragma clang diagnostic ignored "-Warc-performSelector-leaks" + // Start executing the requested task + [target performSelector:method withObject:object]; +#pragma clang diagnostic pop + }]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block completionBlock:(void (^)(void))completion { + dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0); + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:completion]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue { + [self showAnimated:animated whileExecutingBlock:block onQueue:queue completionBlock:NULL]; +} + +- (void)showAnimated:(BOOL)animated whileExecutingBlock:(dispatch_block_t)block onQueue:(dispatch_queue_t)queue completionBlock:(nullable MBProgressHUDCompletionBlock)completion { + self.taskInProgress = YES; + self.completionBlock = completion; + dispatch_async(queue, ^(void) { + block(); + dispatch_async(dispatch_get_main_queue(), ^(void) { + [self cleanUp]; + }); + }); + [self showAnimated:animated]; +} + +- (void)cleanUp { + self.taskInProgress = NO; + [self hideAnimated:self.useAnimation]; +} + +#pragma mark - Labels + +- (NSString *)labelText { + return self.label.text; +} + +- (void)setLabelText:(NSString *)labelText { + MBMainThreadAssert(); + self.label.text = labelText; +} + +- (UIFont *)labelFont { + return self.label.font; +} + +- (void)setLabelFont:(UIFont *)labelFont { + MBMainThreadAssert(); + self.label.font = labelFont; +} + +- (UIColor *)labelColor { + return self.label.textColor; +} + +- (void)setLabelColor:(UIColor *)labelColor { + MBMainThreadAssert(); + self.label.textColor = labelColor; +} + +- (NSString *)detailsLabelText { + return self.detailsLabel.text; +} + +- (void)setDetailsLabelText:(NSString *)detailsLabelText { + MBMainThreadAssert(); + self.detailsLabel.text = detailsLabelText; +} + +- (UIFont *)detailsLabelFont { + return self.detailsLabel.font; +} + +- (void)setDetailsLabelFont:(UIFont *)detailsLabelFont { + MBMainThreadAssert(); + self.detailsLabel.font = detailsLabelFont; +} + +- (UIColor *)detailsLabelColor { + return self.detailsLabel.textColor; +} + +- (void)setDetailsLabelColor:(UIColor *)detailsLabelColor { + MBMainThreadAssert(); + self.detailsLabel.textColor = detailsLabelColor; +} + +- (CGFloat)opacity { + return _opacity; +} + +- (void)setOpacity:(CGFloat)opacity { + MBMainThreadAssert(); + _opacity = opacity; +} + +- (UIColor *)color { + return self.bezelView.color; +} + +- (void)setColor:(UIColor *)color { + MBMainThreadAssert(); + self.bezelView.color = color; +} + +- (CGFloat)yOffset { + return self.offset.y; +} + +- (void)setYOffset:(CGFloat)yOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(self.offset.x, yOffset); +} + +- (CGFloat)xOffset { + return self.offset.x; +} + +- (void)setXOffset:(CGFloat)xOffset { + MBMainThreadAssert(); + self.offset = CGPointMake(xOffset, self.offset.y); +} + +- (CGFloat)cornerRadius { + return self.bezelView.layer.cornerRadius; +} + +- (void)setCornerRadius:(CGFloat)cornerRadius { + MBMainThreadAssert(); + self.bezelView.layer.cornerRadius = cornerRadius; +} + +- (BOOL)dimBackground { + MBBackgroundView *backgroundView = self.backgroundView; + UIColor *dimmedColor = [UIColor colorWithWhite:0.f alpha:.2f]; + return backgroundView.style == MBProgressHUDBackgroundStyleSolidColor && [backgroundView.color isEqual:dimmedColor]; +} + +- (void)setDimBackground:(BOOL)dimBackground { + MBMainThreadAssert(); + self.backgroundView.style = MBProgressHUDBackgroundStyleSolidColor; + self.backgroundView.color = dimBackground ? [UIColor colorWithWhite:0.f alpha:.2f] : [UIColor clearColor]; +} + +- (CGSize)size { + return self.bezelView.frame.size; +} + +- (UIColor *)activityIndicatorColor { + return _activityIndicatorColor; +} + +- (void)setActivityIndicatorColor:(UIColor *)activityIndicatorColor { + if (activityIndicatorColor != _activityIndicatorColor) { + _activityIndicatorColor = activityIndicatorColor; + UIActivityIndicatorView *indicator = (UIActivityIndicatorView *)self.indicator; + if ([indicator isKindOfClass:[UIActivityIndicatorView class]]) { + [indicator setColor:activityIndicatorColor]; + } + } +} + +@end + +@implementation MBProgressHUDRoundedButton + +#pragma mark - Lifecycle + +- (instancetype)initWithFrame:(CGRect)frame { + self = [super initWithFrame:frame]; + if (self) { + CALayer *layer = self.layer; + layer.borderWidth = 1.f; + } + return self; +} + +#pragma mark - Layout + +- (void)layoutSubviews { + [super layoutSubviews]; + // Fully rounded corners + CGFloat height = CGRectGetHeight(self.bounds); + self.layer.cornerRadius = ceil(height / 2.f); +} + +- (CGSize)intrinsicContentSize { + // Only show if we have associated control events + if (self.allControlEvents == 0) return CGSizeZero; + CGSize size = [super intrinsicContentSize]; + // Add some side padding + size.width += 20.f; + return size; +} + +#pragma mark - Color + +- (void)setTitleColor:(UIColor *)color forState:(UIControlState)state { + [super setTitleColor:color forState:state]; + // Update related colors + [self setHighlighted:self.highlighted]; + self.layer.borderColor = color.CGColor; +} + +- (void)setHighlighted:(BOOL)highlighted { + [super setHighlighted:highlighted]; + UIColor *baseColor = [self titleColorForState:UIControlStateSelected]; + self.backgroundColor = highlighted ? [baseColor colorWithAlphaComponent:0.1f] : [UIColor clearColor]; +} + +@end diff --git a/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/README.mdown b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/README.mdown new file mode 100755 index 0000000..21ba843 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/MBProgressHUD/README.mdown @@ -0,0 +1,126 @@ +# MBProgressHUD + +[![Build Status](https://travis-ci.org/matej/MBProgressHUD.svg?branch=master)](https://travis-ci.org/matej/MBProgressHUD) [![codecov.io](https://codecov.io/github/matej/MBProgressHUD/coverage.svg?branch=master)](https://codecov.io/github/matej/MBProgressHUD?branch=master) + [![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) [![CocoaPods compatible](https://img.shields.io/cocoapods/v/MBProgressHUD.svg?style=flat)](https://cocoapods.org/pods/MBProgressHUD) [![License: MIT](https://img.shields.io/cocoapods/l/MBProgressHUD.svg?style=flat)](http://opensource.org/licenses/MIT) + +`MBProgressHUD` is an iOS drop-in class that displays a translucent HUD with an indicator and/or labels while work is being done in a background thread. The HUD is meant as a replacement for the undocumented, private `UIKit` `UIProgressHUD` with some additional features. + +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/1-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/1.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/2-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/2.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/3-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/3.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/4-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/4.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/5-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/5.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/6-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/6.png) +[![](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/7-small.png)](https://raw.githubusercontent.com/wiki/matej/MBProgressHUD/Screenshots/7.png) + +**NOTE:** The class has recently undergone a major rewrite. The old version is available in the [legacy](https://github.com/jdg/MBProgressHUD/tree/legacy) branch, should you need it. + +## Requirements + +`MBProgressHUD` works on iOS 6+ and requires ARC to build. It depends on the following Apple frameworks, which should already be included with most Xcode templates: + +* Foundation.framework +* UIKit.framework +* CoreGraphics.framework + +You will need the latest developer tools in order to build `MBProgressHUD`. Old Xcode versions might work, but compatibility will not be explicitly maintained. + +## Adding MBProgressHUD to your project + +### CocoaPods + +[CocoaPods](http://cocoapods.org) is the recommended way to add MBProgressHUD to your project. + +1. Add a pod entry for MBProgressHUD to your Podfile `pod 'MBProgressHUD', '~> 1.1.0'` +2. Install the pod(s) by running `pod install`. +3. Include MBProgressHUD wherever you need it with `#import "MBProgressHUD.h"`. + +### Carthage + +1. Add MBProgressHUD to your Cartfile. e.g., `github "jdg/MBProgressHUD" ~> 1.1.0` +2. Run `carthage update` +3. Follow the rest of the [standard Carthage installation instructions](https://github.com/Carthage/Carthage#adding-frameworks-to-an-application) to add MBProgressHUD to your project. + +### Source files + +Alternatively you can directly add the `MBProgressHUD.h` and `MBProgressHUD.m` source files to your project. + +1. Download the [latest code version](https://github.com/matej/MBProgressHUD/archive/master.zip) or add the repository as a git submodule to your git-tracked project. +2. Open your project in Xcode, then drag and drop `MBProgressHUD.h` and `MBProgressHUD.m` onto your project (use the "Product Navigator view"). Make sure to select Copy items when asked if you extracted the code archive outside of your project. +3. Include MBProgressHUD wherever you need it with `#import "MBProgressHUD.h"`. + +### Static library + +You can also add MBProgressHUD as a static library to your project or workspace. + +1. Download the [latest code version](https://github.com/matej/MBProgressHUD/downloads) or add the repository as a git submodule to your git-tracked project. +2. Open your project in Xcode, then drag and drop `MBProgressHUD.xcodeproj` onto your project or workspace (use the "Product Navigator view"). +3. Select your target and go to the Build phases tab. In the Link Binary With Libraries section select the add button. On the sheet find and add `libMBProgressHUD.a`. You might also need to add `MBProgressHUD` to the Target Dependencies list. +4. Include MBProgressHUD wherever you need it with `#import `. + +## Usage + +The main guideline you need to follow when dealing with MBProgressHUD while running long-running tasks is keeping the main thread work-free, so the UI can be updated promptly. The recommended way of using MBProgressHUD is therefore to set it up on the main thread and then spinning the task, that you want to perform, off onto a new thread. + +```objective-c +[MBProgressHUD showHUDAddedTo:self.view animated:YES]; +dispatch_async(dispatch_get_global_queue( DISPATCH_QUEUE_PRIORITY_LOW, 0), ^{ + // Do something... + dispatch_async(dispatch_get_main_queue(), ^{ + [MBProgressHUD hideHUDForView:self.view animated:YES]; + }); +}); +``` + +You can add the HUD on any view or window. It is however a good idea to avoid adding the HUD to certain `UIKit` views with complex view hierarchies - like `UITableView` or `UICollectionView`. Those can mutate their subviews in unexpected ways and thereby break HUD display. + +If you need to configure the HUD you can do this by using the MBProgressHUD reference that showHUDAddedTo:animated: returns. + +```objective-c +MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; +hud.mode = MBProgressHUDModeAnnularDeterminate; +hud.label.text = @"Loading"; +[self doSomethingInBackgroundWithProgressCallback:^(float progress) { + hud.progress = progress; +} completionCallback:^{ + [hud hideAnimated:YES]; +}]; +``` + +You can also use a `NSProgress` object and MBProgressHUD will update itself when there is progress reported through that object. + +```objective-c +MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES]; +hud.mode = MBProgressHUDModeAnnularDeterminate; +hud.label.text = @"Loading"; +NSProgress *progress = [self doSomethingInBackgroundCompletion:^{ + [hud hideAnimated:YES]; +}]; +hud.progressObject = progress; +``` + +Keep in mind that UI updates, inclining calls to MBProgressHUD should always be done on the main thread. + +If you need to run your long-running task in the main thread, you should perform it with a slight delay, so UIKit will have enough time to update the UI (i.e., draw the HUD) before you block the main thread with your task. + +```objective-c +[MBProgressHUD showHUDAddedTo:self.view animated:YES]; +dispatch_time_t popTime = dispatch_time(DISPATCH_TIME_NOW, 0.01 * NSEC_PER_SEC); +dispatch_after(popTime, dispatch_get_main_queue(), ^(void){ + // Do something... + [MBProgressHUD hideHUDForView:self.view animated:YES]; +}); +``` + +You should be aware that any HUD updates issued inside the above block won't be displayed until the block completes. + +For more examples, including how to use MBProgressHUD with asynchronous operations such as NSURLConnection, take a look at the bundled demo project. Extensive API documentation is provided in the header file (MBProgressHUD.h). + + +## License + +This code is distributed under the terms and conditions of the [MIT license](LICENSE). + +## Change-log + +A brief summary of each MBProgressHUD release can be found in the [CHANGELOG](CHANGELOG.mdown). diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Manifest.lock b/Go-Jek/GO-JEK-Assignment/Pods/Manifest.lock new file mode 100755 index 0000000..2641023 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Manifest.lock @@ -0,0 +1,16 @@ +PODS: + - MBProgressHUD (1.1.0) + +DEPENDENCIES: + - MBProgressHUD (~> 1.1.0) + +SPEC REPOS: + https://github.com/cocoapods/specs.git: + - MBProgressHUD + +SPEC CHECKSUMS: + MBProgressHUD: e7baa36a220447d8aeb12769bf0585582f3866d9 + +PODFILE CHECKSUM: ace7c7ec8272833eaeda20e1d138b7206d558901 + +COCOAPODS: 1.7.5 diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Pods.xcodeproj/project.pbxproj b/Go-Jek/GO-JEK-Assignment/Pods/Pods.xcodeproj/project.pbxproj new file mode 100755 index 0000000..ecbc031 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Pods.xcodeproj/project.pbxproj @@ -0,0 +1,911 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 51; + objects = { + +/* Begin PBXBuildFile section */ + 0737CC99BECA5A4AB69055E18F4CD3C0 /* Pods-GJAssignment-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 54F54F51B4A8364D05EFF42D8609F3EB /* Pods-GJAssignment-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 43008B43C24B308D27CDB3B9D221587D /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A8ACEBAADC0881BE2CD6B3232F2FCC2 /* Foundation.framework */; }; + 567BA6759E74EF2CB29E00D02550F15E /* Pods-GJAssignmentTests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 35B3B57E91D0623AAD23A4870D083014 /* Pods-GJAssignmentTests-dummy.m */; }; + 64F46B6A8DBA885AF570A54A082D1BAA /* QuartzCore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FFA51DF58BFD369AF6BE252245A18B2F /* QuartzCore.framework */; }; + 70EE1C774C978E1876AE6CA1224E897D /* MBProgressHUD.m in Sources */ = {isa = PBXBuildFile; fileRef = 6152B0CD59AAF2557B3AA4375B61BEED /* MBProgressHUD.m */; }; + 81B9E2F21B1EF9FE4E769103108DEA8A /* MBProgressHUD-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = 071047A4A08135548333750E08C06DA7 /* MBProgressHUD-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + 962312AC0055E94AB99871A2B22EF68F /* Pods-GJAssignmentUITests-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 299980D999158E74E64527905B472D6D /* Pods-GJAssignmentUITests-dummy.m */; }; + A601994621C892656F60D1A1292A9565 /* CoreGraphics.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = AD27B1C1B5D2F31F501108838D28EF02 /* CoreGraphics.framework */; }; + CCAEBBD6F779CFCC7C16BB5D6F5C5F00 /* Pods-GJAssignmentTests-umbrella.h in Headers */ = {isa = PBXBuildFile; fileRef = B485E75BEB29B1517ABB558EF40939E6 /* Pods-GJAssignmentTests-umbrella.h */; settings = {ATTRIBUTES = (Public, ); }; }; + D732128F532E272BF056069F3540BB2A /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A8ACEBAADC0881BE2CD6B3232F2FCC2 /* Foundation.framework */; }; + DABF20F73D27572CD97071A6D4DB9004 /* MBProgressHUD-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 657678C8C3FB4725CEE10D8E8607F1EE /* MBProgressHUD-dummy.m */; }; + EB2A4C29BE958CCB03B3FD9FAB927F1F /* MBProgressHUD.h in Headers */ = {isa = PBXBuildFile; fileRef = E6584E1984A3CC79C447B818C6F79920 /* MBProgressHUD.h */; settings = {ATTRIBUTES = (Public, ); }; }; + FDEE610D05646030A9DF3309DC513580 /* Pods-GJAssignment-dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = AE06BCC7AA8C406800FC9F8961268787 /* Pods-GJAssignment-dummy.m */; }; + FF0EE7E6F869E55DA268F25622674EB9 /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 8A8ACEBAADC0881BE2CD6B3232F2FCC2 /* Foundation.framework */; }; +/* End PBXBuildFile section */ + +/* Begin PBXContainerItemProxy section */ + DC1D42AD3AF298B4A0E241F812A23585 /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 8586362265635A26D1DA2F8D6B3D0A3F; + remoteInfo = "Pods-GJAssignment"; + }; + DCFAED809D988E505ECDB4EFADB5E42B /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = BFDFE7DC352907FC980B868725387E98 /* Project object */; + proxyType = 1; + remoteGlobalIDString = 82B0A41D3031FF27D78E17B0A9A46FB0; + remoteInfo = MBProgressHUD; + }; +/* End PBXContainerItemProxy section */ + +/* Begin PBXFileReference section */ + 071047A4A08135548333750E08C06DA7 /* MBProgressHUD-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MBProgressHUD-umbrella.h"; sourceTree = ""; }; + 0FC512D03937BF1DB067FE372E7571F1 /* Pods-GJAssignment.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignment.release.xcconfig"; sourceTree = ""; }; + 2579CDFE7A1A8113EE18E4512156EF02 /* Pods-GJAssignment-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-GJAssignment-acknowledgements.markdown"; sourceTree = ""; }; + 299980D999158E74E64527905B472D6D /* Pods-GJAssignmentUITests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-GJAssignmentUITests-dummy.m"; sourceTree = ""; }; + 314A8B77E12FA60EC7A3A26BD6902E97 /* MBProgressHUD-prefix.pch */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "MBProgressHUD-prefix.pch"; sourceTree = ""; }; + 35B3B57E91D0623AAD23A4870D083014 /* Pods-GJAssignmentTests-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-GJAssignmentTests-dummy.m"; sourceTree = ""; }; + 37433B4C27995E647E2455DA64B5AD9C /* Pods-GJAssignment-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GJAssignment-Info.plist"; sourceTree = ""; }; + 37ABD8DCAA991ADD9AA4DDA40D85379D /* MBProgressHUD.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = MBProgressHUD.modulemap; sourceTree = ""; }; + 392BA88A2B93D9DF3095A1E872C0CA7D /* Pods-GJAssignmentUITests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignmentUITests.release.xcconfig"; sourceTree = ""; }; + 4C51EA6F8C4497F8FD0DE092272724F9 /* libPods-GJAssignmentUITests.a */ = {isa = PBXFileReference; explicitFileType = archive.ar; includeInIndex = 0; name = "libPods-GJAssignmentUITests.a"; path = "libPods-GJAssignmentUITests.a"; sourceTree = BUILT_PRODUCTS_DIR; }; + 54F54F51B4A8364D05EFF42D8609F3EB /* Pods-GJAssignment-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-GJAssignment-umbrella.h"; sourceTree = ""; }; + 55E64675FA81EA2D1A3BDBE921BA7BDE /* Pods-GJAssignmentTests.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-GJAssignmentTests.modulemap"; sourceTree = ""; }; + 5CC409F2D792F2C9EF43A4B89646BD77 /* Pods-GJAssignmentUITests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GJAssignmentUITests-acknowledgements.plist"; sourceTree = ""; }; + 5CFDC6E1C2FE7AE5AA715ED19ADE2A19 /* Pods-GJAssignmentUITests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-GJAssignmentUITests-acknowledgements.markdown"; sourceTree = ""; }; + 6152B0CD59AAF2557B3AA4375B61BEED /* MBProgressHUD.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = MBProgressHUD.m; sourceTree = ""; }; + 619D2E1B8E7310902C83396D0E12E72A /* MBProgressHUD-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "MBProgressHUD-Info.plist"; sourceTree = ""; }; + 6551FE3C081181EB0B22AD1893C403CE /* Pods-GJAssignment.modulemap */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.module; path = "Pods-GJAssignment.modulemap"; sourceTree = ""; }; + 657678C8C3FB4725CEE10D8E8607F1EE /* MBProgressHUD-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "MBProgressHUD-dummy.m"; sourceTree = ""; }; + 65889C0119C36C428CA8684D1A66588D /* Pods-GJAssignment.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignment.debug.xcconfig"; sourceTree = ""; }; + 673051F63E8147999A418796D3D19DC7 /* Pods-GJAssignmentTests-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GJAssignmentTests-acknowledgements.plist"; sourceTree = ""; }; + 83300827127A8483A1232554E16A2243 /* Pods-GJAssignmentTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignmentTests.release.xcconfig"; sourceTree = ""; }; + 850FF76381C7E65E1AF730D36F519546 /* MBProgressHUD.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = MBProgressHUD.xcconfig; sourceTree = ""; }; + 8A8ACEBAADC0881BE2CD6B3232F2FCC2 /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/Foundation.framework; sourceTree = DEVELOPER_DIR; }; + 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = MBProgressHUD.framework; path = MBProgressHUD.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 8C4254696D97A9CC4D19FA77760F597D /* Pods-GJAssignmentTests-Info.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GJAssignmentTests-Info.plist"; sourceTree = ""; }; + 9D6A9B23F7FE6674FF8CDEBA7F814985 /* Pods-GJAssignmentTests-acknowledgements.markdown */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text; path = "Pods-GJAssignmentTests-acknowledgements.markdown"; sourceTree = ""; }; + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */ = {isa = PBXFileReference; explicitFileType = text.script.ruby; includeInIndex = 1; indentWidth = 2; lastKnownFileType = text; name = Podfile; path = ../Podfile; sourceTree = SOURCE_ROOT; tabWidth = 2; xcLanguageSpecificationIdentifier = xcode.lang.ruby; }; + A1F574C2E02EFF6FDEBD6B3B2FA11524 /* Pods-GJAssignment-frameworks.sh */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.script.sh; path = "Pods-GJAssignment-frameworks.sh"; sourceTree = ""; }; + AD27B1C1B5D2F31F501108838D28EF02 /* CoreGraphics.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreGraphics.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/CoreGraphics.framework; sourceTree = DEVELOPER_DIR; }; + AE06BCC7AA8C406800FC9F8961268787 /* Pods-GJAssignment-dummy.m */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.objc; path = "Pods-GJAssignment-dummy.m"; sourceTree = ""; }; + B485E75BEB29B1517ABB558EF40939E6 /* Pods-GJAssignmentTests-umbrella.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = "Pods-GJAssignmentTests-umbrella.h"; sourceTree = ""; }; + B6FA54D5908F27BAE0DCC21C3EB54553 /* Pods-GJAssignment-acknowledgements.plist */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.plist.xml; path = "Pods-GJAssignment-acknowledgements.plist"; sourceTree = ""; }; + B79B29BE7023EC24CFE900491803E1CD /* Pods-GJAssignmentTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignmentTests.debug.xcconfig"; sourceTree = ""; }; + C3A51ADDB5DB1B30097379CBEF930DB0 /* Pods_GJAssignment.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_GJAssignment.framework; path = "Pods-GJAssignment.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + C9A594E02C11BF625DE9DA2300B3AF51 /* Pods_GJAssignmentTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; name = Pods_GJAssignmentTests.framework; path = "Pods-GJAssignmentTests.framework"; sourceTree = BUILT_PRODUCTS_DIR; }; + E6584E1984A3CC79C447B818C6F79920 /* MBProgressHUD.h */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = sourcecode.c.h; path = MBProgressHUD.h; sourceTree = ""; }; + F6997F2461C85F7710CB50154E5E4066 /* Pods-GJAssignmentUITests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; path = "Pods-GJAssignmentUITests.debug.xcconfig"; sourceTree = ""; }; + FFA51DF58BFD369AF6BE252245A18B2F /* QuartzCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = QuartzCore.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS12.2.sdk/System/Library/Frameworks/QuartzCore.framework; sourceTree = DEVELOPER_DIR; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + 1AA3C16B4B3AB40CA74BDDADEF5C143A /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + A601994621C892656F60D1A1292A9565 /* CoreGraphics.framework in Frameworks */, + FF0EE7E6F869E55DA268F25622674EB9 /* Foundation.framework in Frameworks */, + 64F46B6A8DBA885AF570A54A082D1BAA /* QuartzCore.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 33B6E0EDC1977486D851B3B91A2242A8 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 43008B43C24B308D27CDB3B9D221587D /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + C73BFD62F590AF5239F76DD6A9D4C7AC /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + DB0C94FFF3B47A417CCC26C50BEFD3D4 /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + D732128F532E272BF056069F3540BB2A /* Foundation.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + 03008EA7FA88E465302796FC1C541B71 /* MBProgressHUD */ = { + isa = PBXGroup; + children = ( + E6584E1984A3CC79C447B818C6F79920 /* MBProgressHUD.h */, + 6152B0CD59AAF2557B3AA4375B61BEED /* MBProgressHUD.m */, + 972C05D4ABF4AFE0028A9907AE29C8E3 /* Support Files */, + ); + name = MBProgressHUD; + path = MBProgressHUD; + sourceTree = ""; + }; + 03C5C200A0787E300053CFA8F53CA094 /* Frameworks */ = { + isa = PBXGroup; + children = ( + F04009B2B08E830E0DAA2A9E1245D6B8 /* iOS */, + ); + name = Frameworks; + sourceTree = ""; + }; + 3F242479102E24C5847AB040CB8095A8 /* Pods */ = { + isa = PBXGroup; + children = ( + 03008EA7FA88E465302796FC1C541B71 /* MBProgressHUD */, + ); + name = Pods; + sourceTree = ""; + }; + 7D87F7ED7EE12AB4422547D4439922BC /* Pods-GJAssignmentTests */ = { + isa = PBXGroup; + children = ( + 55E64675FA81EA2D1A3BDBE921BA7BDE /* Pods-GJAssignmentTests.modulemap */, + 9D6A9B23F7FE6674FF8CDEBA7F814985 /* Pods-GJAssignmentTests-acknowledgements.markdown */, + 673051F63E8147999A418796D3D19DC7 /* Pods-GJAssignmentTests-acknowledgements.plist */, + 35B3B57E91D0623AAD23A4870D083014 /* Pods-GJAssignmentTests-dummy.m */, + 8C4254696D97A9CC4D19FA77760F597D /* Pods-GJAssignmentTests-Info.plist */, + B485E75BEB29B1517ABB558EF40939E6 /* Pods-GJAssignmentTests-umbrella.h */, + B79B29BE7023EC24CFE900491803E1CD /* Pods-GJAssignmentTests.debug.xcconfig */, + 83300827127A8483A1232554E16A2243 /* Pods-GJAssignmentTests.release.xcconfig */, + ); + name = "Pods-GJAssignmentTests"; + path = "Target Support Files/Pods-GJAssignmentTests"; + sourceTree = ""; + }; + 85C128E0210B55C7AFE1AB1CBBAD93AC /* Pods-GJAssignmentUITests */ = { + isa = PBXGroup; + children = ( + 5CFDC6E1C2FE7AE5AA715ED19ADE2A19 /* Pods-GJAssignmentUITests-acknowledgements.markdown */, + 5CC409F2D792F2C9EF43A4B89646BD77 /* Pods-GJAssignmentUITests-acknowledgements.plist */, + 299980D999158E74E64527905B472D6D /* Pods-GJAssignmentUITests-dummy.m */, + F6997F2461C85F7710CB50154E5E4066 /* Pods-GJAssignmentUITests.debug.xcconfig */, + 392BA88A2B93D9DF3095A1E872C0CA7D /* Pods-GJAssignmentUITests.release.xcconfig */, + ); + name = "Pods-GJAssignmentUITests"; + path = "Target Support Files/Pods-GJAssignmentUITests"; + sourceTree = ""; + }; + 972C05D4ABF4AFE0028A9907AE29C8E3 /* Support Files */ = { + isa = PBXGroup; + children = ( + 37ABD8DCAA991ADD9AA4DDA40D85379D /* MBProgressHUD.modulemap */, + 850FF76381C7E65E1AF730D36F519546 /* MBProgressHUD.xcconfig */, + 657678C8C3FB4725CEE10D8E8607F1EE /* MBProgressHUD-dummy.m */, + 619D2E1B8E7310902C83396D0E12E72A /* MBProgressHUD-Info.plist */, + 314A8B77E12FA60EC7A3A26BD6902E97 /* MBProgressHUD-prefix.pch */, + 071047A4A08135548333750E08C06DA7 /* MBProgressHUD-umbrella.h */, + ); + name = "Support Files"; + path = "../Target Support Files/MBProgressHUD"; + sourceTree = ""; + }; + BA7820F4DB094AE38ACDBC8F02203744 /* Targets Support Files */ = { + isa = PBXGroup; + children = ( + DAC2A2C492C49213028433028FE8C611 /* Pods-GJAssignment */, + 7D87F7ED7EE12AB4422547D4439922BC /* Pods-GJAssignmentTests */, + 85C128E0210B55C7AFE1AB1CBBAD93AC /* Pods-GJAssignmentUITests */, + ); + name = "Targets Support Files"; + sourceTree = ""; + }; + CF1408CF629C7361332E53B88F7BD30C = { + isa = PBXGroup; + children = ( + 9D940727FF8FB9C785EB98E56350EF41 /* Podfile */, + 03C5C200A0787E300053CFA8F53CA094 /* Frameworks */, + 3F242479102E24C5847AB040CB8095A8 /* Pods */, + E8DFE1305B28FBA8A2DDC125102B943A /* Products */, + BA7820F4DB094AE38ACDBC8F02203744 /* Targets Support Files */, + ); + sourceTree = ""; + }; + DAC2A2C492C49213028433028FE8C611 /* Pods-GJAssignment */ = { + isa = PBXGroup; + children = ( + 6551FE3C081181EB0B22AD1893C403CE /* Pods-GJAssignment.modulemap */, + 2579CDFE7A1A8113EE18E4512156EF02 /* Pods-GJAssignment-acknowledgements.markdown */, + B6FA54D5908F27BAE0DCC21C3EB54553 /* Pods-GJAssignment-acknowledgements.plist */, + AE06BCC7AA8C406800FC9F8961268787 /* Pods-GJAssignment-dummy.m */, + A1F574C2E02EFF6FDEBD6B3B2FA11524 /* Pods-GJAssignment-frameworks.sh */, + 37433B4C27995E647E2455DA64B5AD9C /* Pods-GJAssignment-Info.plist */, + 54F54F51B4A8364D05EFF42D8609F3EB /* Pods-GJAssignment-umbrella.h */, + 65889C0119C36C428CA8684D1A66588D /* Pods-GJAssignment.debug.xcconfig */, + 0FC512D03937BF1DB067FE372E7571F1 /* Pods-GJAssignment.release.xcconfig */, + ); + name = "Pods-GJAssignment"; + path = "Target Support Files/Pods-GJAssignment"; + sourceTree = ""; + }; + E8DFE1305B28FBA8A2DDC125102B943A /* Products */ = { + isa = PBXGroup; + children = ( + 4C51EA6F8C4497F8FD0DE092272724F9 /* libPods-GJAssignmentUITests.a */, + 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */, + C3A51ADDB5DB1B30097379CBEF930DB0 /* Pods_GJAssignment.framework */, + C9A594E02C11BF625DE9DA2300B3AF51 /* Pods_GJAssignmentTests.framework */, + ); + name = Products; + sourceTree = ""; + }; + F04009B2B08E830E0DAA2A9E1245D6B8 /* iOS */ = { + isa = PBXGroup; + children = ( + AD27B1C1B5D2F31F501108838D28EF02 /* CoreGraphics.framework */, + 8A8ACEBAADC0881BE2CD6B3232F2FCC2 /* Foundation.framework */, + FFA51DF58BFD369AF6BE252245A18B2F /* QuartzCore.framework */, + ); + name = iOS; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXHeadersBuildPhase section */ + 47F343FDAD8E5F63886188082EAB99E0 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 5CD42970A7E9A45EB811936029C122D9 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + CCAEBBD6F779CFCC7C16BB5D6F5C5F00 /* Pods-GJAssignmentTests-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 8F0EBAB2761523F18BB73EB4DC5CBEC3 /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 81B9E2F21B1EF9FE4E769103108DEA8A /* MBProgressHUD-umbrella.h in Headers */, + EB2A4C29BE958CCB03B3FD9FAB927F1F /* MBProgressHUD.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 907147AF91644AF88DADD5E2BF87A8EF /* Headers */ = { + isa = PBXHeadersBuildPhase; + buildActionMask = 2147483647; + files = ( + 0737CC99BECA5A4AB69055E18F4CD3C0 /* Pods-GJAssignment-umbrella.h in Headers */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXHeadersBuildPhase section */ + +/* Begin PBXNativeTarget section */ + 82B0A41D3031FF27D78E17B0A9A46FB0 /* MBProgressHUD */ = { + isa = PBXNativeTarget; + buildConfigurationList = 70191416B9573C7ABB42333A08FA2DE4 /* Build configuration list for PBXNativeTarget "MBProgressHUD" */; + buildPhases = ( + 8F0EBAB2761523F18BB73EB4DC5CBEC3 /* Headers */, + D2F84DC2E2DB3628B17B3841F2837477 /* Sources */, + 1AA3C16B4B3AB40CA74BDDADEF5C143A /* Frameworks */, + 435BF4D16B2F9CAB6E7F883F30E32B74 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = MBProgressHUD; + productName = MBProgressHUD; + productReference = 8B8FAB0D627B17EDE1366984278705D9 /* MBProgressHUD.framework */; + productType = "com.apple.product-type.framework"; + }; + 8586362265635A26D1DA2F8D6B3D0A3F /* Pods-GJAssignment */ = { + isa = PBXNativeTarget; + buildConfigurationList = 64DC9EFADF7413EB84847E9DF8FC1C3C /* Build configuration list for PBXNativeTarget "Pods-GJAssignment" */; + buildPhases = ( + 907147AF91644AF88DADD5E2BF87A8EF /* Headers */, + 896D356C1E29BB97D378C6124B3EB337 /* Sources */, + DB0C94FFF3B47A417CCC26C50BEFD3D4 /* Frameworks */, + 9389EC48474E1568CA01EBCB2CDA8652 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + CC4DB53653A48B1001416F9016E8A824 /* PBXTargetDependency */, + ); + name = "Pods-GJAssignment"; + productName = "Pods-GJAssignment"; + productReference = C3A51ADDB5DB1B30097379CBEF930DB0 /* Pods_GJAssignment.framework */; + productType = "com.apple.product-type.framework"; + }; + 89070513BF64E60E3C8FCB8E668873C2 /* Pods-GJAssignmentTests */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7C0DD646DDB3ECD2DC736FADE73242A /* Build configuration list for PBXNativeTarget "Pods-GJAssignmentTests" */; + buildPhases = ( + 5CD42970A7E9A45EB811936029C122D9 /* Headers */, + 4E8EA58C94A5EA1C651DCD61C3FA263A /* Sources */, + 33B6E0EDC1977486D851B3B91A2242A8 /* Frameworks */, + CDE927D8C248403E981DFF801BCAB2F7 /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + 88A8F5F4D27997E5C47F152B8C7D06BF /* PBXTargetDependency */, + ); + name = "Pods-GJAssignmentTests"; + productName = "Pods-GJAssignmentTests"; + productReference = C9A594E02C11BF625DE9DA2300B3AF51 /* Pods_GJAssignmentTests.framework */; + productType = "com.apple.product-type.framework"; + }; + 94A4A85F49BD3759CE8D1F00CCFA08A9 /* Pods-GJAssignmentUITests */ = { + isa = PBXNativeTarget; + buildConfigurationList = F1F1AE0A8D138EFF0C921555E45C5064 /* Build configuration list for PBXNativeTarget "Pods-GJAssignmentUITests" */; + buildPhases = ( + 47F343FDAD8E5F63886188082EAB99E0 /* Headers */, + D930A1FBA67E3881FC2EF07144ED22B0 /* Sources */, + C73BFD62F590AF5239F76DD6A9D4C7AC /* Frameworks */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = "Pods-GJAssignmentUITests"; + productName = "Pods-GJAssignmentUITests"; + productReference = 4C51EA6F8C4497F8FD0DE092272724F9 /* libPods-GJAssignmentUITests.a */; + productType = "com.apple.product-type.library.static"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + BFDFE7DC352907FC980B868725387E98 /* Project object */ = { + isa = PBXProject; + attributes = { + LastSwiftUpdateCheck = 1100; + LastUpgradeCheck = 1100; + }; + buildConfigurationList = 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */; + compatibilityVersion = "Xcode 10.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + ); + mainGroup = CF1408CF629C7361332E53B88F7BD30C; + productRefGroup = E8DFE1305B28FBA8A2DDC125102B943A /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + 82B0A41D3031FF27D78E17B0A9A46FB0 /* MBProgressHUD */, + 8586362265635A26D1DA2F8D6B3D0A3F /* Pods-GJAssignment */, + 89070513BF64E60E3C8FCB8E668873C2 /* Pods-GJAssignmentTests */, + 94A4A85F49BD3759CE8D1F00CCFA08A9 /* Pods-GJAssignmentUITests */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + 435BF4D16B2F9CAB6E7F883F30E32B74 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 9389EC48474E1568CA01EBCB2CDA8652 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; + CDE927D8C248403E981DFF801BCAB2F7 /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + 4E8EA58C94A5EA1C651DCD61C3FA263A /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 567BA6759E74EF2CB29E00D02550F15E /* Pods-GJAssignmentTests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + 896D356C1E29BB97D378C6124B3EB337 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + FDEE610D05646030A9DF3309DC513580 /* Pods-GJAssignment-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D2F84DC2E2DB3628B17B3841F2837477 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + DABF20F73D27572CD97071A6D4DB9004 /* MBProgressHUD-dummy.m in Sources */, + 70EE1C774C978E1876AE6CA1224E897D /* MBProgressHUD.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; + D930A1FBA67E3881FC2EF07144ED22B0 /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 962312AC0055E94AB99871A2B22EF68F /* Pods-GJAssignmentUITests-dummy.m in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin PBXTargetDependency section */ + 88A8F5F4D27997E5C47F152B8C7D06BF /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = "Pods-GJAssignment"; + target = 8586362265635A26D1DA2F8D6B3D0A3F /* Pods-GJAssignment */; + targetProxy = DC1D42AD3AF298B4A0E241F812A23585 /* PBXContainerItemProxy */; + }; + CC4DB53653A48B1001416F9016E8A824 /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + name = MBProgressHUD; + target = 82B0A41D3031FF27D78E17B0A9A46FB0 /* MBProgressHUD */; + targetProxy = DCFAED809D988E505ECDB4EFADB5E42B /* PBXContainerItemProxy */; + }; +/* End PBXTargetDependency section */ + +/* Begin XCBuildConfiguration section */ + 07E1F28B8AD6C8F07EE6F7727E39E4E5 /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 850FF76381C7E65E1AF730D36F519546 /* MBProgressHUD.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD.modulemap"; + PRODUCT_MODULE_NAME = MBProgressHUD; + PRODUCT_NAME = MBProgressHUD; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 257497152829C177993B5EC99C1D227A /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_NO_COMMON_BLOCKS = YES; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_RELEASE=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_COMPILATION_MODE = wholemodule; + SWIFT_OPTIMIZATION_LEVEL = "-O"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Release; + }; + 2A97AE61D755F5BFD2A489643483661F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 83300827127A8483A1232554E16A2243 /* Pods-GJAssignmentTests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 43CDA65CA7C4F3FD3B3131BB84FD92F7 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = B79B29BE7023EC24CFE900491803E1CD /* Pods-GJAssignmentTests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + 69C42E94C86A6A7E10BDB9E70E762ADC /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 0FC512D03937BF1DB067FE372E7571F1 /* Pods-GJAssignment.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Release; + }; + 6E863F9D334037E082D231B06CA9DC1F /* Release */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 392BA88A2B93D9DF3095A1E872C0CA7D /* Pods-GJAssignmentUITests.release.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + A786902994C30700C1A40C3FA49FCC12 /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 850FF76381C7E65E1AF730D36F519546 /* MBProgressHUD.xcconfig */; + buildSettings = { + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + GCC_PREFIX_HEADER = "Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch"; + INFOPLIST_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 8.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MODULEMAP_FILE = "Target Support Files/MBProgressHUD/MBProgressHUD.modulemap"; + PRODUCT_MODULE_NAME = MBProgressHUD; + PRODUCT_NAME = MBProgressHUD; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "$(inherited) "; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + A9EACD53BCAFDB1292422B2266B5C9FE /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = 65889C0119C36C428CA8684D1A66588D /* Pods-GJAssignment.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = ""; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + CURRENT_PROJECT_VERSION = 1; + DEFINES_MODULE = YES; + DYLIB_COMPATIBILITY_VERSION = 1; + DYLIB_CURRENT_VERSION = 1; + DYLIB_INSTALL_NAME_BASE = "@rpath"; + INFOPLIST_FILE = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist"; + INSTALL_PATH = "$(LOCAL_LIBRARY_DIR)/Frameworks"; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@loader_path/Frameworks", + ); + MACH_O_TYPE = staticlib; + MODULEMAP_FILE = "Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap"; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + VERSIONING_SYSTEM = "apple-generic"; + VERSION_INFO_PREFIX = ""; + }; + name = Debug; + }; + DD8F832993327D1DD8046C3CBCBD97CD /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; + CLANG_CXX_LIBRARY = "libc++"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + GCC_C_LANGUAGE_STANDARD = gnu11; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "POD_CONFIGURATION_DEBUG=1", + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + PRODUCT_NAME = "$(TARGET_NAME)"; + STRIP_INSTALLED_PRODUCT = NO; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + SWIFT_VERSION = 5.0; + SYMROOT = "${SRCROOT}/../build"; + }; + name = Debug; + }; + F3B41BA009812C84C4D13BDD919C8D2D /* Debug */ = { + isa = XCBuildConfiguration; + baseConfigurationReference = F6997F2461C85F7710CB50154E5E4066 /* Pods-GJAssignmentUITests.debug.xcconfig */; + buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = NO; + CLANG_ENABLE_OBJC_WEAK = NO; + CODE_SIGN_IDENTITY = "iPhone Developer"; + "CODE_SIGN_IDENTITY[sdk=appletvos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = ""; + "CODE_SIGN_IDENTITY[sdk=watchos*]" = ""; + IPHONEOS_DEPLOYMENT_TARGET = 11.0; + MACH_O_TYPE = staticlib; + OTHER_LDFLAGS = ""; + OTHER_LIBTOOLFLAGS = ""; + PODS_ROOT = "$(SRCROOT)"; + PRODUCT_BUNDLE_IDENTIFIER = "org.cocoapods.${PRODUCT_NAME:rfc1034identifier}"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + 4821239608C13582E20E6DA73FD5F1F9 /* Build configuration list for PBXProject "Pods" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + DD8F832993327D1DD8046C3CBCBD97CD /* Debug */, + 257497152829C177993B5EC99C1D227A /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 64DC9EFADF7413EB84847E9DF8FC1C3C /* Build configuration list for PBXNativeTarget "Pods-GJAssignment" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A9EACD53BCAFDB1292422B2266B5C9FE /* Debug */, + 69C42E94C86A6A7E10BDB9E70E762ADC /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + 70191416B9573C7ABB42333A08FA2DE4 /* Build configuration list for PBXNativeTarget "MBProgressHUD" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + A786902994C30700C1A40C3FA49FCC12 /* Debug */, + 07E1F28B8AD6C8F07EE6F7727E39E4E5 /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7C0DD646DDB3ECD2DC736FADE73242A /* Build configuration list for PBXNativeTarget "Pods-GJAssignmentTests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 43CDA65CA7C4F3FD3B3131BB84FD92F7 /* Debug */, + 2A97AE61D755F5BFD2A489643483661F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + F1F1AE0A8D138EFF0C921555E45C5064 /* Build configuration list for PBXNativeTarget "Pods-GJAssignmentUITests" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + F3B41BA009812C84C4D13BDD919C8D2D /* Debug */, + 6E863F9D334037E082D231B06CA9DC1F /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + }; + rootObject = BFDFE7DC352907FC980B868725387E98 /* Project object */; +} diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist new file mode 100755 index 0000000..21a30b4 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.1.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m new file mode 100755 index 0000000..67a74df --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_MBProgressHUD : NSObject +@end +@implementation PodsDummy_MBProgressHUD +@end diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch new file mode 100755 index 0000000..beb2a24 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-prefix.pch @@ -0,0 +1,12 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h new file mode 100755 index 0000000..8522a01 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD-umbrella.h @@ -0,0 +1,17 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + +#import "MBProgressHUD.h" + +FOUNDATION_EXPORT double MBProgressHUDVersionNumber; +FOUNDATION_EXPORT const unsigned char MBProgressHUDVersionString[]; + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap new file mode 100755 index 0000000..dbb3f94 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.modulemap @@ -0,0 +1,6 @@ +framework module MBProgressHUD { + umbrella header "MBProgressHUD-umbrella.h" + + export * + module * { export * } +} diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig new file mode 100755 index 0000000..021d75c --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/MBProgressHUD/MBProgressHUD.xcconfig @@ -0,0 +1,9 @@ +CONFIGURATION_BUILD_DIR = ${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "QuartzCore" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_ROOT = ${SRCROOT} +PODS_TARGET_SRCROOT = ${PODS_ROOT}/MBProgressHUD +PRODUCT_BUNDLE_IDENTIFIER = org.cocoapods.${PRODUCT_NAME:rfc1034identifier} +SKIP_INSTALL = YES diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist new file mode 100755 index 0000000..2243fe6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.markdown b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.markdown new file mode 100755 index 0000000..fc54ce3 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.markdown @@ -0,0 +1,25 @@ +# Acknowledgements +This application makes use of the following third party libraries: + +## MBProgressHUD + +Copyright © 2009-2016 Matej Bukovinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. +Generated by CocoaPods - https://cocoapods.org diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.plist new file mode 100755 index 0000000..ac488bb --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-acknowledgements.plist @@ -0,0 +1,57 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Copyright © 2009-2016 Matej Bukovinski + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. + License + MIT + Title + MBProgressHUD + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-dummy.m b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-dummy.m new file mode 100755 index 0000000..e307bf7 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_GJAssignment : NSObject +@end +@implementation PodsDummy_Pods_GJAssignment +@end diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-input-files.xcfilelist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-input-files.xcfilelist new file mode 100755 index 0000000..1102ae3 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh +${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-output-files.xcfilelist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-output-files.xcfilelist new file mode 100755 index 0000000..f2fcefe --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Debug-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-input-files.xcfilelist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-input-files.xcfilelist new file mode 100755 index 0000000..1102ae3 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-input-files.xcfilelist @@ -0,0 +1,2 @@ +${PODS_ROOT}/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh +${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-output-files.xcfilelist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-output-files.xcfilelist new file mode 100755 index 0000000..f2fcefe --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks-Release-output-files.xcfilelist @@ -0,0 +1 @@ +${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}/MBProgressHUD.framework \ No newline at end of file diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh new file mode 100755 index 0000000..6d86817 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-frameworks.sh @@ -0,0 +1,171 @@ +#!/bin/sh +set -e +set -u +set -o pipefail + +function on_error { + echo "$(realpath -mq "${0}"):$1: error: Unexpected failure" +} +trap 'on_error $LINENO' ERR + +if [ -z ${FRAMEWORKS_FOLDER_PATH+x} ]; then + # If FRAMEWORKS_FOLDER_PATH is not set, then there's nowhere for us to copy + # frameworks to, so exit 0 (signalling the script phase was successful). + exit 0 +fi + +echo "mkdir -p ${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" +mkdir -p "${CONFIGURATION_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + +COCOAPODS_PARALLEL_CODE_SIGN="${COCOAPODS_PARALLEL_CODE_SIGN:-false}" +SWIFT_STDLIB_PATH="${DT_TOOLCHAIN_DIR}/usr/lib/swift/${PLATFORM_NAME}" + +# Used as a return value for each invocation of `strip_invalid_archs` function. +STRIP_BINARY_RETVAL=0 + +# This protects against multiple targets copying the same framework dependency at the same time. The solution +# was originally proposed here: https://lists.samba.org/archive/rsync/2008-February/020158.html +RSYNC_PROTECT_TMP_FILES=(--filter "P .*.??????") + +# Copies and strips a vendored framework +install_framework() +{ + if [ -r "${BUILT_PRODUCTS_DIR}/$1" ]; then + local source="${BUILT_PRODUCTS_DIR}/$1" + elif [ -r "${BUILT_PRODUCTS_DIR}/$(basename "$1")" ]; then + local source="${BUILT_PRODUCTS_DIR}/$(basename "$1")" + elif [ -r "$1" ]; then + local source="$1" + fi + + local destination="${TARGET_BUILD_DIR}/${FRAMEWORKS_FOLDER_PATH}" + + if [ -L "${source}" ]; then + echo "Symlinked..." + source="$(readlink "${source}")" + fi + + # Use filter instead of exclude so missing patterns don't throw errors. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${destination}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${destination}" + + local basename + basename="$(basename -s .framework "$1")" + binary="${destination}/${basename}.framework/${basename}" + + if ! [ -r "$binary" ]; then + binary="${destination}/${basename}" + elif [ -L "${binary}" ]; then + echo "Destination binary is symlinked..." + dirname="$(dirname "${binary}")" + binary="${dirname}/$(readlink "${binary}")" + fi + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"dynamically linked shared library"* ]]; then + strip_invalid_archs "$binary" + fi + + # Resign the code if required by the build settings to avoid unstable apps + code_sign_if_enabled "${destination}/$(basename "$1")" + + # Embed linked Swift runtime libraries. No longer necessary as of Xcode 7. + if [ "${XCODE_VERSION_MAJOR}" -lt 7 ]; then + local swift_runtime_libs + swift_runtime_libs=$(xcrun otool -LX "$binary" | grep --color=never @rpath/libswift | sed -E s/@rpath\\/\(.+dylib\).*/\\1/g | uniq -u) + for lib in $swift_runtime_libs; do + echo "rsync -auv \"${SWIFT_STDLIB_PATH}/${lib}\" \"${destination}\"" + rsync -auv "${SWIFT_STDLIB_PATH}/${lib}" "${destination}" + code_sign_if_enabled "${destination}/${lib}" + done + fi +} + +# Copies and strips a vendored dSYM +install_dsym() { + local source="$1" + if [ -r "$source" ]; then + # Copy the dSYM into a the targets temp dir. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${source}\" \"${DERIVED_FILES_DIR}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${source}" "${DERIVED_FILES_DIR}" + + local basename + basename="$(basename -s .framework.dSYM "$source")" + binary="${DERIVED_FILES_DIR}/${basename}.framework.dSYM/Contents/Resources/DWARF/${basename}" + + # Strip invalid architectures so "fat" simulator / device frameworks work on device + if [[ "$(file "$binary")" == *"Mach-O "*"dSYM companion"* ]]; then + strip_invalid_archs "$binary" + fi + + if [[ $STRIP_BINARY_RETVAL == 1 ]]; then + # Move the stripped file into its final destination. + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter \"- CVS/\" --filter \"- .svn/\" --filter \"- .git/\" --filter \"- .hg/\" --filter \"- Headers\" --filter \"- PrivateHeaders\" --filter \"- Modules\" \"${DERIVED_FILES_DIR}/${basename}.framework.dSYM\" \"${DWARF_DSYM_FOLDER_PATH}\"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${DERIVED_FILES_DIR}/${basename}.framework.dSYM" "${DWARF_DSYM_FOLDER_PATH}" + else + # The dSYM was not stripped at all, in this case touch a fake folder so the input/output paths from Xcode do not reexecute this script because the file is missing. + touch "${DWARF_DSYM_FOLDER_PATH}/${basename}.framework.dSYM" + fi + fi +} + +# Copies the bcsymbolmap files of a vendored framework +install_bcsymbolmap() { + local bcsymbolmap_path="$1" + local destination="${BUILT_PRODUCTS_DIR}" + echo "rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}"" + rsync --delete -av "${RSYNC_PROTECT_TMP_FILES[@]}" --filter "- CVS/" --filter "- .svn/" --filter "- .git/" --filter "- .hg/" --filter "- Headers" --filter "- PrivateHeaders" --filter "- Modules" "${bcsymbolmap_path}" "${destination}" +} + +# Signs a framework with the provided identity +code_sign_if_enabled() { + if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" -a "${CODE_SIGNING_REQUIRED:-}" != "NO" -a "${CODE_SIGNING_ALLOWED}" != "NO" ]; then + # Use the current code_sign_identity + echo "Code Signing $1 with Identity ${EXPANDED_CODE_SIGN_IDENTITY_NAME}" + local code_sign_cmd="/usr/bin/codesign --force --sign ${EXPANDED_CODE_SIGN_IDENTITY} ${OTHER_CODE_SIGN_FLAGS:-} --preserve-metadata=identifier,entitlements '$1'" + + if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + code_sign_cmd="$code_sign_cmd &" + fi + echo "$code_sign_cmd" + eval "$code_sign_cmd" + fi +} + +# Strip invalid architectures +strip_invalid_archs() { + binary="$1" + # Get architectures for current target binary + binary_archs="$(lipo -info "$binary" | rev | cut -d ':' -f1 | awk '{$1=$1;print}' | rev)" + # Intersect them with the architectures we are building for + intersected_archs="$(echo ${ARCHS[@]} ${binary_archs[@]} | tr ' ' '\n' | sort | uniq -d)" + # If there are no archs supported by this binary then warn the user + if [[ -z "$intersected_archs" ]]; then + echo "warning: [CP] Vendored binary '$binary' contains architectures ($binary_archs) none of which match the current build architectures ($ARCHS)." + STRIP_BINARY_RETVAL=0 + return + fi + stripped="" + for arch in $binary_archs; do + if ! [[ "${ARCHS}" == *"$arch"* ]]; then + # Strip non-valid architectures in-place + lipo -remove "$arch" -output "$binary" "$binary" + stripped="$stripped $arch" + fi + done + if [[ "$stripped" ]]; then + echo "Stripped $binary of architectures:$stripped" + fi + STRIP_BINARY_RETVAL=1 +} + + +if [[ "$CONFIGURATION" == "Debug" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework" +fi +if [[ "$CONFIGURATION" == "Release" ]]; then + install_framework "${BUILT_PRODUCTS_DIR}/MBProgressHUD/MBProgressHUD.framework" +fi +if [ "${COCOAPODS_PARALLEL_CODE_SIGN}" == "true" ]; then + wait +fi diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-umbrella.h b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-umbrella.h new file mode 100755 index 0000000..5be1e2a --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_GJAssignmentVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentVersionString[]; + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.debug.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.debug.xcconfig new file mode 100755 index 0000000..0c279dc --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.debug.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap new file mode 100755 index 0000000..ef1416d --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.modulemap @@ -0,0 +1,6 @@ +framework module Pods_GJAssignment { + umbrella header "Pods-GJAssignment-umbrella.h" + + export * + module * { export * } +} diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.release.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.release.xcconfig new file mode 100755 index 0000000..0c279dc --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignment/Pods-GJAssignment.release.xcconfig @@ -0,0 +1,9 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" +LD_RUNPATH_SEARCH_PATHS = $(inherited) '@executable_path/Frameworks' '@loader_path/Frameworks' +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist new file mode 100755 index 0000000..2243fe6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.markdown b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.markdown new file mode 100755 index 0000000..102af75 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.plist new file mode 100755 index 0000000..7acbad1 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-dummy.m b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-dummy.m new file mode 100755 index 0000000..2ef1570 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_GJAssignmentTests : NSObject +@end +@implementation PodsDummy_Pods_GJAssignmentTests +@end diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-umbrella.h b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-umbrella.h new file mode 100755 index 0000000..7e4d811 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_GJAssignmentTestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentTestsVersionString[]; + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.debug.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.debug.xcconfig new file mode 100755 index 0000000..e6ff70a --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.debug.xcconfig @@ -0,0 +1,8 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap new file mode 100755 index 0000000..149ecd8 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_GJAssignmentTests { + umbrella header "Pods-GJAssignmentTests-umbrella.h" + + export * + module * { export * } +} diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.release.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.release.xcconfig new file mode 100755 index 0000000..e6ff70a --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentTests/Pods-GJAssignmentTests.release.xcconfig @@ -0,0 +1,8 @@ +FRAMEWORK_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD" +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +HEADER_SEARCH_PATHS = $(inherited) "${PODS_CONFIGURATION_BUILD_DIR}/MBProgressHUD/MBProgressHUD.framework/Headers" +OTHER_LDFLAGS = $(inherited) -framework "CoreGraphics" -framework "MBProgressHUD" -framework "QuartzCore" +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-Info.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-Info.plist new file mode 100755 index 0000000..2243fe6 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-Info.plist @@ -0,0 +1,26 @@ + + + + + CFBundleDevelopmentRegion + en + CFBundleExecutable + ${EXECUTABLE_NAME} + CFBundleIdentifier + ${PRODUCT_BUNDLE_IDENTIFIER} + CFBundleInfoDictionaryVersion + 6.0 + CFBundleName + ${PRODUCT_NAME} + CFBundlePackageType + FMWK + CFBundleShortVersionString + 1.0.0 + CFBundleSignature + ???? + CFBundleVersion + ${CURRENT_PROJECT_VERSION} + NSPrincipalClass + + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.markdown b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.markdown new file mode 100755 index 0000000..102af75 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.markdown @@ -0,0 +1,3 @@ +# Acknowledgements +This application makes use of the following third party libraries: +Generated by CocoaPods - https://cocoapods.org diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.plist b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.plist new file mode 100755 index 0000000..7acbad1 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-acknowledgements.plist @@ -0,0 +1,29 @@ + + + + + PreferenceSpecifiers + + + FooterText + This application makes use of the following third party libraries: + Title + Acknowledgements + Type + PSGroupSpecifier + + + FooterText + Generated by CocoaPods - https://cocoapods.org + Title + + Type + PSGroupSpecifier + + + StringsTable + Acknowledgements + Title + Acknowledgements + + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-dummy.m b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-dummy.m new file mode 100755 index 0000000..dc1eba7 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-dummy.m @@ -0,0 +1,5 @@ +#import +@interface PodsDummy_Pods_GJAssignmentUITests : NSObject +@end +@implementation PodsDummy_Pods_GJAssignmentUITests +@end diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-umbrella.h b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-umbrella.h new file mode 100755 index 0000000..13483ae --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests-umbrella.h @@ -0,0 +1,16 @@ +#ifdef __OBJC__ +#import +#else +#ifndef FOUNDATION_EXPORT +#if defined(__cplusplus) +#define FOUNDATION_EXPORT extern "C" +#else +#define FOUNDATION_EXPORT extern +#endif +#endif +#endif + + +FOUNDATION_EXPORT double Pods_GJAssignmentUITestsVersionNumber; +FOUNDATION_EXPORT const unsigned char Pods_GJAssignmentUITestsVersionString[]; + diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.debug.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.debug.xcconfig new file mode 100755 index 0000000..521bc0e --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.debug.xcconfig @@ -0,0 +1,5 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.modulemap b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.modulemap new file mode 100755 index 0000000..23f4187 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.modulemap @@ -0,0 +1,6 @@ +framework module Pods_GJAssignmentUITests { + umbrella header "Pods-GJAssignmentUITests-umbrella.h" + + export * + module * { export * } +} diff --git a/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.release.xcconfig b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.release.xcconfig new file mode 100755 index 0000000..521bc0e --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/Pods/Target Support Files/Pods-GJAssignmentUITests/Pods-GJAssignmentUITests.release.xcconfig @@ -0,0 +1,5 @@ +GCC_PREPROCESSOR_DEFINITIONS = $(inherited) COCOAPODS=1 +PODS_BUILD_DIR = ${BUILD_DIR} +PODS_CONFIGURATION_BUILD_DIR = ${PODS_BUILD_DIR}/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME) +PODS_PODFILE_DIR_PATH = ${SRCROOT}/. +PODS_ROOT = ${SRCROOT}/Pods diff --git a/Go-Jek/GO-JEK-Assignment/README.md b/Go-Jek/GO-JEK-Assignment/README.md new file mode 100755 index 0000000..b817931 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/README.md @@ -0,0 +1,50 @@ +## 1. GO-JEK Assignment +GO-JEK Assignment by [Anonymous](mailto://). + +![](docs/Screenshot.png) + +## 2. Requirements +- iOS 11.0+ +- [Xcode 10.3](https://download.developer.apple.com/Developer_Tools/Xcode_10.3/Xcode_10.3.xip) +- [SwiftLint](https://github.com/realm/SwiftLint) +- [CocoaPods](https://cocoapods.org/) +- [Fastlane](https://fastlane.tools)(Optional) + +## 3. Getting Start +- Open `GJAssignment.xcworkspace` in Xcode 10.3 +- Build the project + +## 4. Problem Statements +[Problem Statements](docs/ProblemStatement.pdf) + +## 5. Swift +This project is build using Swift 5. + +## 6. 3rd Party +- [MBProgressHUD](https://github.com/jdg/MBProgressHUD) + +​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). + +## 7. Unit and UI Test case + - Total number of test cases - **`34`** + - Unit test cases - **`20`** + - UI test cases - **`14`** + - Code coverage - **`87.3%`** + +I'm not using any 3rd parting to mocking and stubs the objects. I build my own protocol based solution by creating fake `URLSession`. +![](docs/XcodeTestResult.png) +![](docs/FastlaneTestResult.png) + +## 8. Architecture +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). + +## 9. Memory Management +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`). +![](docs/MemoryLeaks.png) + +## 10. Extra Feature +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. + +## 11. Fastlane +- `fastlane tests` - Runs all the tests +- `fastlane build` - Run all the tests and build diff --git a/Go-Jek/GO-JEK-Assignment/docs/FastlaneTestResult.png b/Go-Jek/GO-JEK-Assignment/docs/FastlaneTestResult.png new file mode 100755 index 0000000..4cb52d7 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/docs/FastlaneTestResult.png differ diff --git a/Go-Jek/GO-JEK-Assignment/docs/MemoryLeaks.png b/Go-Jek/GO-JEK-Assignment/docs/MemoryLeaks.png new file mode 100755 index 0000000..eb96ede Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/docs/MemoryLeaks.png differ diff --git a/Go-Jek/GO-JEK-Assignment/docs/ProblemStatement.pdf b/Go-Jek/GO-JEK-Assignment/docs/ProblemStatement.pdf new file mode 100755 index 0000000..c397ba8 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/docs/ProblemStatement.pdf differ diff --git a/Go-Jek/GO-JEK-Assignment/docs/Screenshot.png b/Go-Jek/GO-JEK-Assignment/docs/Screenshot.png new file mode 100755 index 0000000..1f27037 Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/docs/Screenshot.png differ diff --git a/Go-Jek/GO-JEK-Assignment/docs/XcodeTestResult.png b/Go-Jek/GO-JEK-Assignment/docs/XcodeTestResult.png new file mode 100755 index 0000000..5aef7cb Binary files /dev/null and b/Go-Jek/GO-JEK-Assignment/docs/XcodeTestResult.png differ diff --git a/Go-Jek/GO-JEK-Assignment/fastlane/Appfile b/Go-Jek/GO-JEK-Assignment/fastlane/Appfile new file mode 100755 index 0000000..1803063 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/fastlane/Appfile @@ -0,0 +1,6 @@ +# app_identifier("[[APP_IDENTIFIER]]") # The bundle identifier of your app +# apple_id("[[APPLE_ID]]") # Your Apple email address + + +# For more information about the Appfile, see: +# https://docs.fastlane.tools/advanced/#appfile diff --git a/Go-Jek/GO-JEK-Assignment/fastlane/Fastfile b/Go-Jek/GO-JEK-Assignment/fastlane/Fastfile new file mode 100755 index 0000000..5fcab13 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/fastlane/Fastfile @@ -0,0 +1,14 @@ +default_platform(:ios) + +platform :ios do + desc "Runs all the tests" + lane :tests do + scan(devices:"iPhone X") + end + + desc "Run all the tests and build" + lane :build do + scan(devices:"iPhone X") + gym + end +end diff --git a/Go-Jek/GO-JEK-Assignment/fastlane/README.md b/Go-Jek/GO-JEK-Assignment/fastlane/README.md new file mode 100755 index 0000000..3e4dd25 --- /dev/null +++ b/Go-Jek/GO-JEK-Assignment/fastlane/README.md @@ -0,0 +1,44 @@ +fastlane documentation +================ +# Installation + +Make sure you have the latest version of the Xcode command line tools installed: + +``` +xcode-select --install +``` + +Install _fastlane_ using +``` +[sudo] gem install fastlane -NV +``` +or alternatively using `brew cask install fastlane` + +# Available Actions +## iOS +### ios tests +``` +fastlane ios tests +``` +Runs all the tests +### ios build +``` +fastlane ios build +``` +Run all the tests and build +### ios update +``` +fastlane ios update +``` +Update 3rd party dependencies +### ios setup +``` +fastlane ios setup +``` +Setup Project + +---- + +This README.md is auto-generated and will be re-generated every time [fastlane](https://fastlane.tools) is run. +More information about fastlane can be found on [fastlane.tools](https://fastlane.tools). +The documentation of fastlane can be found on [docs.fastlane.tools](https://docs.fastlane.tools).