├── .gitignore ├── README.md ├── api ├── address_search_results.json ├── home_restaurant_list.json ├── menu_item_details.json └── restaurant_details.json ├── screenshots ├── screenshot-1.jpeg ├── screenshot-2.jpeg ├── screenshot-3.jpeg ├── screenshot-4.png ├── screenshot-5.png └── screenshot-6.png └── solutions ├── devsprint-1 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Extensions │ │ └── Data+jSONDecode.swift │ ├── Models │ │ └── Address.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ └── AddressSearchViewController.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ ├── Models │ │ │ │ ├── Restaurant.swift │ │ │ │ └── RestaurantsDetails.swift │ │ │ ├── RestaurantListViewController.swift │ │ │ └── ViewModels │ │ │ │ └── RestaurantsViewModel.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ └── DeliveryApi.swift └── DeliveryAppChallengeTests │ └── DeliveryAppChallengeTests.swift ├── devsprint-2 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Models │ │ ├── Address.swift │ │ └── Restaurant.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ ├── AddressSearchViewController.swift │ │ │ └── AddressSearchViewModel.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ ├── RestaurantDetailsViewController.swift │ │ │ └── RestaurantDetailsViewModel.swift │ │ ├── RestaurantList │ │ │ └── RestaurantListViewController.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ ├── APIError.swift │ │ ├── APIManager.swift │ │ ├── DataSourceUserDefaults.swift │ │ ├── DeliveryApi.swift │ │ └── Protocols │ │ └── APIManagerProtocol.swift └── DeliveryAppChallengeTests │ ├── APIManagerTestCase.swift │ ├── DataSourceUserDefaultsTestCase.swift │ ├── DeliveryAppChallengeTests.swift │ ├── Mock │ ├── APIManagerMock.swift │ ├── MockRestaurantDetailsPresentable.swift │ └── restaurant_details.json │ └── RestaurantDetailsViewModelTests.swift ├── devsprint-maria-eugenia-1 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppCoordinator.swift │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Coordinator │ │ └── Coordinator.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ └── AddressSearchViewController.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── Model │ │ │ │ │ ├── Menu.swift │ │ │ │ │ └── RestauranteMenuModel.swift │ │ │ │ ├── View │ │ │ │ │ ├── MenuCellView.swift │ │ │ │ │ └── MenuListView.swift │ │ │ │ └── ViewModel │ │ │ │ │ └── MenuViewModel.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── Coordinator │ │ │ │ └── RestaurantListCoordinator.swift │ │ │ │ ├── Model │ │ │ │ ├── DeliveryTime.swift │ │ │ │ └── RestaurantModel.swift │ │ │ │ ├── View │ │ │ │ ├── RestaurantListView.swift │ │ │ │ └── cell │ │ │ │ │ └── RestaurantCellView.swift │ │ │ │ └── ViewModel │ │ │ │ └── RestaurantListViewModel.swift │ │ ├── Home │ │ │ ├── Coordinator │ │ │ │ └── HomeViewCoordinator.swift │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ └── RestaurantListViewController.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ ├── ApiManager.swift │ │ └── DeliveryApi.swift └── DeliveryAppChallengeTests │ └── DeliveryAppChallengeTests.swift ├── devsprint-matheus-reis-2 ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Coordinator │ │ ├── AppCoordinator.swift │ │ ├── Coordinator.swift │ │ └── HomeCoordinator.swift │ ├── Models │ │ ├── Address.swift │ │ ├── MenuItem.swift │ │ ├── Restaurant.swift │ │ └── RestaurantDetail.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── devpass-logo-blue copy 2-1.png │ │ │ │ ├── devpass-logo-blue copy 2.png │ │ │ │ ├── devpass-logo-blue copy 3.png │ │ │ │ ├── devpass-logo-blue copy 4.png │ │ │ │ ├── devpass-logo-blue copy 5.png │ │ │ │ └── devpass-logo-blue copy.png │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ ├── AddressSearchViewController.swift │ │ │ └── AddressSearchViewModel.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ ├── HomeViewController.swift │ │ │ └── HomeViewModel.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ └── RestaurantListViewController.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ ├── SettingsViewController.swift │ │ │ └── SettingsViewModel.swift │ └── Service │ │ ├── DeliveryApi.swift │ │ ├── DeliveryApiError.swift │ │ └── UserInfo.swift ├── DeliveryAppChallengeTests │ ├── HomeViewModelTests.swift │ ├── Info.plist │ └── Mock │ │ └── DeliveryApiMock.swift ├── Podfile ├── Podfile.lock └── project.yml ├── devsprint-matheus-reis-3 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Models │ │ ├── Address.swift │ │ ├── MenuItem.swift │ │ └── Restaurant.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── devpass-logo-blue copy 2-1.png │ │ │ │ ├── devpass-logo-blue copy 2.png │ │ │ │ ├── devpass-logo-blue copy 3.png │ │ │ │ ├── devpass-logo-blue copy 4.png │ │ │ │ ├── devpass-logo-blue copy 5.png │ │ │ │ └── devpass-logo-blue copy.png │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ └── AddressSearchViewController.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ ├── RestaurantListViewController.swift │ │ │ └── RestaurantListViewModel.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ ├── Delivery │ │ ├── DeliveryApi.swift │ │ ├── DeliveryApiProtocol.swift │ │ └── DeliveryApiRequest.swift │ │ └── Networking │ │ ├── NetworkManager.swift │ │ └── ServiceError.swift └── DeliveryAppChallengeTests │ └── DeliveryAppChallengeTests.swift ├── devsprint-matheus-reis-6 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Models │ │ ├── Address.swift │ │ ├── MenuItemDetails.swift │ │ ├── Restaurant.swift │ │ └── RestaurantDetails.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── devpass-logo-blue copy 2-1.png │ │ │ │ ├── devpass-logo-blue copy 2.png │ │ │ │ ├── devpass-logo-blue copy 3.png │ │ │ │ ├── devpass-logo-blue copy 4.png │ │ │ │ ├── devpass-logo-blue copy 5.png │ │ │ │ └── devpass-logo-blue copy.png │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ ├── AddressSearchViewController.swift │ │ │ └── AddressSearchViewModel.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ └── RestaurantListViewController.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ ├── DeliveryApi.swift │ │ ├── DeliveryRequest.swift │ │ ├── NetworkError.swift │ │ ├── NetworkManager.swift │ │ └── RequestProtocol.swift └── DeliveryAppChallengeTests │ └── DeliveryAppChallengeTests.swift ├── devsprint-pedro-menezes-2 ├── DeliveryAppChallenge.xcodeproj │ ├── project.pbxproj │ ├── project.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ └── xcshareddata │ │ └── xcschemes │ │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge │ ├── AppDelegate │ │ ├── AppDelegate.swift │ │ └── SceneDelegate.swift │ ├── Models │ │ ├── Address.swift │ │ ├── MenuItemDetails.swift │ │ ├── Restaurant.swift │ │ └── RestaurantDetails.swift │ ├── Resources │ │ ├── Assets.xcassets │ │ │ ├── AccentColor.colorset │ │ │ │ └── Contents.json │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── Contents.json │ │ │ │ ├── devpass-logo-blue copy 2-1.png │ │ │ │ ├── devpass-logo-blue copy 2.png │ │ │ │ ├── devpass-logo-blue copy 3.png │ │ │ │ ├── devpass-logo-blue copy 4.png │ │ │ │ ├── devpass-logo-blue copy 5.png │ │ │ │ └── devpass-logo-blue copy.png │ │ │ ├── Contents.json │ │ │ ├── pizza.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── pizza.jpeg │ │ │ └── restaurant-logo.imageset │ │ │ │ ├── Contents.json │ │ │ │ └── restaurante-logo.jpeg │ │ ├── Base.lproj │ │ │ └── LaunchScreen.storyboard │ │ └── Info.plist │ ├── Screens │ │ ├── AddressSearch │ │ │ └── AddressSearchViewController.swift │ │ ├── Components │ │ │ ├── AddressListView │ │ │ │ ├── AddressCellView.swift │ │ │ │ └── AddressListView.swift │ │ │ ├── AddressView │ │ │ │ └── AddressView.swift │ │ │ ├── CategoryListView │ │ │ │ ├── CategoryCellView.swift │ │ │ │ └── CategoryListView.swift │ │ │ ├── MenuListView │ │ │ │ ├── MenuCellView.swift │ │ │ │ └── MenuListView.swift │ │ │ ├── RatingView │ │ │ │ └── RatingView.swift │ │ │ ├── RestaurantInfoView │ │ │ │ └── RestaurantInfoView.swift │ │ │ └── RestaurantListView │ │ │ │ ├── RestaurantCellView.swift │ │ │ │ └── RestaurantListView.swift │ │ ├── Home │ │ │ ├── HomeView.swift │ │ │ └── HomeViewController.swift │ │ ├── MenuItem │ │ │ ├── MenuItemView.swift │ │ │ └── MenuItemViewController.swift │ │ ├── RestaurantDetails │ │ │ ├── RestaurantDetailsView.swift │ │ │ └── RestaurantDetailsViewController.swift │ │ ├── RestaurantList │ │ │ └── RestaurantListViewController.swift │ │ └── Settings │ │ │ ├── SettingsView.swift │ │ │ └── SettingsViewController.swift │ └── Service │ │ └── DeliveryApi.swift └── DeliveryAppChallengeTests │ └── DeliveryAppChallengeTests.swift └── devsprint-vinicius-carvalho-1 ├── DeliveryAppChallenge.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved └── xcshareddata │ └── xcschemes │ └── DeliveryAppChallenge.xcscheme ├── DeliveryAppChallenge ├── AppDelegate │ ├── AppDelegate.swift │ └── SceneDelegate.swift ├── Coordinators │ ├── AppCoordinator.swift │ ├── Coordinatable.swift │ ├── Coordinator.swift │ ├── HomeCoordinator.swift │ ├── Settings.swift │ └── SettingsCoordinator.swift ├── Extension │ └── Data+Resource.swift ├── Models │ ├── Address.swift │ ├── DeliveryTime.swift │ ├── Restaurant.swift │ ├── RestaurantItem.swift │ ├── RestaurantsListModel.swift │ └── Review.swift ├── Resources │ ├── Assets.xcassets │ │ ├── AccentColor.colorset │ │ │ └── Contents.json │ │ ├── AppIcon.appiconset │ │ │ └── Contents.json │ │ ├── Contents.json │ │ ├── pizza.imageset │ │ │ ├── Contents.json │ │ │ └── pizza.jpeg │ │ └── restaurant-logo.imageset │ │ │ ├── Contents.json │ │ │ └── restaurante-logo.jpeg │ ├── Base.lproj │ │ └── LaunchScreen.storyboard │ ├── Info.plist │ └── restaurant-details.json ├── Screens │ ├── AddressSearch │ │ └── AddressSearchViewController.swift │ ├── Components │ │ ├── AddressListView │ │ │ ├── AddressCellView.swift │ │ │ └── AddressListView.swift │ │ ├── AddressView │ │ │ └── AddressView.swift │ │ ├── CategoryListView │ │ │ ├── CategoryCellView.swift │ │ │ └── CategoryListView.swift │ │ ├── MenuListView │ │ │ ├── MenuCellView.swift │ │ │ └── MenuListView.swift │ │ ├── RatingView │ │ │ └── RatingView.swift │ │ ├── RestaurantInfoView │ │ │ └── RestaurantInfoView.swift │ │ └── RestaurantListView │ │ │ ├── RestaurantCellView.swift │ │ │ └── RestaurantListView.swift │ ├── Home │ │ ├── HomeView.swift │ │ ├── HomeViewController.swift │ │ ├── HomeViewModel.swift │ │ └── HomeViewModelType.swift │ ├── MenuItem │ │ ├── MenuItemView.swift │ │ └── MenuItemViewController.swift │ ├── RestaurantDetails │ │ ├── RestaurantDetailsView.swift │ │ ├── RestaurantDetailsViewController.swift │ │ └── RestaurantDetailsViewModel.swift │ ├── RestaurantList │ │ └── RestaurantListViewController.swift │ └── Settings │ │ ├── SettigsViewModelType.swift │ │ ├── SettingsView.swift │ │ ├── SettingsViewController.swift │ │ ├── SettingsViewModel.swift │ │ └── SettingsViewModelType.swift └── Service │ ├── DeliveryApi.swift │ ├── Network │ ├── DeliveryApi.swift │ ├── DeliveryService.swift │ ├── NetworkManager.swift │ ├── Router.swift │ └── ServiceError.swift │ ├── Persistence │ ├── DefaultsKey.swift │ └── DefaultsManager.swift │ ├── User.swift │ └── UserDefaults.swift └── DeliveryAppChallengeTests ├── DeliveryApiTests.swift ├── DeliveryAppChallengeTests.swift ├── DeliveryServiceTests.swift ├── Mocks ├── APIServiceMock.swift ├── ApiServiceSpy.swift ├── DeliveryApiServiceStub.swift ├── RestaurantDetailsPresentableMock.swift └── URLProtocolMock.swift ├── RestaurantDetailsViewModelTests.swift ├── Service └── DeliveryApiTests.swift ├── ServiceManagerTests.swift └── SettingsViewModelTests.swift /README.md: -------------------------------------------------------------------------------- 1 | 2 | # MVVM Challenge - Delivery App 🍕 3 | 4 | Neste desafio, aplicaremos conceitos da arquitetura MVVM para finalizar a implementação de um aplicativo. 5 | 6 | Desenvolveremos ViewModels e seus testes, camadas de integração com APIs, navegação entre telas e padrões de comunicação entre as várias camadas da arquitetura. 7 | 8 |

9 | drawing 10 | drawing 11 | drawing 12 | drawing 13 | drawing 14 | drawing 15 |

16 | 17 | # Iniciando os trabalhos 18 | 19 | 20 | 1. Clone este repositório na sua máquina. 21 | 2. Abra a pasta `solutions` e acesse o projeto referente à sua Sprint. 22 | 3. Faça um build e rode o projeto. ▶️ 23 | 24 | # Sobre a Devpass 25 | 26 | A Devpass é uma comunidade de pessoas desenvolvedoras de alto potencial acelerando suas carreiras através de desenvolvimento de produtos reais, como o Hereminders, e conexões com as principais lideranças de tecnologia do país, através de mentorias particulares e Tech Talks. 27 | 28 | Se interessou? Acesse www.devpass.com.br e se inscreva na nossa lista para ficar por dentro das novidades! 29 | -------------------------------------------------------------------------------- /api/address_search_results.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "street": "Rua Augusta", 4 | "number": "495", 5 | "neighborhood": "Consolação", 6 | }, 7 | { 8 | "street": "Rua Alba", 9 | "number": "108", 10 | "neighborhood": "Parque Jabaquara", 11 | }, 12 | { 13 | "street": "Rua Aurora", 14 | "number": "462", 15 | "neighborhood": "Santa Ifigênia", 16 | }, 17 | { 18 | "street": "Rua Alberto Cintra", 19 | "number": "392", 20 | "neighborhood": "União", 21 | }, 22 | { 23 | "street": "Rua Aspicuelta", 24 | "number": "245", 25 | "neighborhood": "Alto de Pinheiros", 26 | }, 27 | { 28 | "street": "Rua Bresser", 29 | "number": "1087", 30 | "neighborhood": "Brás", 31 | }, 32 | { 33 | "street": "Rua Barão de Ladário", 34 | "number": "409", 35 | "neighborhood": "Brás", 36 | }, 37 | { 38 | "street": "Rua Bela Cintra", 39 | "number": "859", 40 | "neighborhood": "Consolação", 41 | }, 42 | { 43 | "street": "Rua Borges Lagoa", 44 | "number": "120", 45 | "neighborhood": "Vila Clementino", 46 | }, 47 | { 48 | "street": "Rua Barata Ribeiro", 49 | "number": "710", 50 | "neighborhood": "Copacabana", 51 | }, 52 | 53 | ] 54 | 55 | -------------------------------------------------------------------------------- /api/home_restaurant_list.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Benjamin a Padaria", 4 | "category": "Padaria", 5 | "delivery_time": { 6 | "min": 23, 7 | "max": 33 8 | } 9 | }, 10 | { 11 | "name": "Pecorino a Trattoria", 12 | "category": "Italiana", 13 | "delivery_time": { 14 | "min": 38, 15 | "max": 48 16 | } 17 | }, 18 | { 19 | "name": "Bar do Alemão", 20 | "category": "Brasileira", 21 | "delivery_time": { 22 | "min": 42, 23 | "max": 52 24 | } 25 | }, 26 | { 27 | "name": "Benjamin a Padaria", 28 | "category": "Padaria", 29 | "delivery_time": { 30 | "min": 23, 31 | "max": 33 32 | } 33 | }, 34 | { 35 | "name": "Abbraccio", 36 | "category": "Italiana", 37 | "delivery_time": { 38 | "min": 65, 39 | "max": 75 40 | } 41 | }, 42 | { 43 | "name": "Poke Garden", 44 | "category": "Japonesa", 45 | "delivery_time": { 46 | "min": 36, 47 | "max": 46 48 | } 49 | }, 50 | { 51 | "name": "Bar do Juarez", 52 | "category": "Brasileira", 53 | "delivery_time": { 54 | "min": 41, 55 | "max": 51 56 | } 57 | }, 58 | { 59 | "name": "Tradi Vila Nova", 60 | "category": "Hambúrguer", 61 | "delivery_time": { 62 | "min": 28, 63 | "max": 38 64 | } 65 | }, 66 | { 67 | "name": "1900 Pizzeria", 68 | "category": "Pizza", 69 | "delivery_time": { 70 | "min": 45, 71 | "max": 55 72 | } 73 | }, 74 | { 75 | "name": "Estella Burger", 76 | "category": "Hambúrguer", 77 | "delivery_time": { 78 | "min": 17, 79 | "max": 27 80 | } 81 | }, 82 | { 83 | "name": "Tacomex", 84 | "category": "Mexicana", 85 | "delivery_time": { 86 | "min": 17, 87 | "max": 27 88 | } 89 | } 90 | ] -------------------------------------------------------------------------------- /api/menu_item_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Copo Pão de Queijo + Suco de Laranja", 3 | "description": "Combo com 8 mini pães de queijo + Suco de Laranja", 4 | "price": 20.00 5 | } -------------------------------------------------------------------------------- /api/restaurant_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Benjamin A Padaria", 3 | "category": "Padaria", 4 | "delivery_time": { 5 | "min": 23, 6 | "max": 33 7 | }, 8 | "reviews": { 9 | "score": 4.8, 10 | "count": 351 11 | }, 12 | "menu": [ 13 | { 14 | "category": "Café da manhã", 15 | "name": "Copo Pão de Queijo + Suco de Laranja", 16 | "price": 20 17 | }, 18 | { 19 | "category": "Café da manhã", 20 | "name": "Iogurte granola e mel", 21 | "price": 12 22 | }, 23 | { 24 | "category": "Café da manhã", 25 | "name": "Pão francês com manteiga na chapa", 26 | "price": 5 27 | }, 28 | { 29 | "category": "Café da manhã", 30 | "name": "Pão na chapa com requeijão", 31 | "price": 8 32 | }, 33 | { 34 | "category": "Café da manhã", 35 | "name": "Café com leite", 36 | "price": 9 37 | }, 38 | { 39 | "category": "Almoço", 40 | "name": "Wrap de Cogumelo", 41 | "price": 24 42 | }, 43 | { 44 | "category": "Almoço", 45 | "name": "Torta de Palmito", 46 | "price": 15 47 | }, 48 | { 49 | "category": "Almoço", 50 | "name": "Torta de Frango", 51 | "price": 15 52 | }, 53 | { 54 | "category": "Almoço", 55 | "name": "Quiche de tomate fresco", 56 | "price": 15 57 | }, 58 | { 59 | "category": "Jantar", 60 | "name": "Combo 3 Sopas + Pão Filão", 61 | "price": 87 62 | }, 63 | { 64 | "category": "Jantar", 65 | "name": "Sanduíche de pernil", 66 | "price": 22 67 | } 68 | ] 69 | } -------------------------------------------------------------------------------- /screenshots/screenshot-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-1.jpeg -------------------------------------------------------------------------------- /screenshots/screenshot-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-2.jpeg -------------------------------------------------------------------------------- /screenshots/screenshot-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-3.jpeg -------------------------------------------------------------------------------- /screenshots/screenshot-4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-4.png -------------------------------------------------------------------------------- /screenshots/screenshot-5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-5.png -------------------------------------------------------------------------------- /screenshots/screenshot-6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/screenshots/screenshot-6.png -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | // 32 | } 33 | 34 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | self.window?.rootViewController = UINavigationController(rootViewController: HomeViewController()) 21 | self.window?.windowScene = windowScene 22 | self.window?.makeKeyAndVisible() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Extensions/Data+jSONDecode.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+jSONDecode.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Dairan on 08/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Data { 11 | func jSONDecode(using strategy: JSONDecoder.KeyDecodingStrategy) -> T? { 12 | let decoder = JSONDecoder() 13 | decoder.keyDecodingStrategy = strategy 14 | do { 15 | return try decoder.decode(T.self, from: self) 16 | } catch let error { 17 | return nil 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by joao camargo on 09/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address : Codable { 11 | let street: String 12 | let number: String 13 | let neighborhood: String 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIApplicationSceneManifest 11 | 12 | UIApplicationSupportsMultipleScenes 13 | 14 | UISceneConfigurations 15 | 16 | UIWindowSceneSessionRoleApplication 17 | 18 | 19 | UISceneConfigurationName 20 | Default Configuration 21 | UISceneDelegateClassName 22 | $(PRODUCT_MODULE_NAME).SceneDelegate 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | // MARK: - HomeViewController 11 | 12 | class HomeViewController: UIViewController { 13 | init() { 14 | super.init(nibName: nil, bundle: nil) 15 | 16 | navigationItem.title = "Delivery App" 17 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 18 | style: .plain, 19 | target: nil, 20 | action: nil) 21 | } 22 | 23 | required init?(coder: NSCoder) { 24 | fatalError("init(coder:) has not been implemented") 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | RestaurantsViewModel() 30 | } 31 | 32 | override func loadView() { 33 | self.view = HomeView() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/RestaurantList/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Dairan on 08/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | // MARK: - Restaurant 11 | 12 | struct Restaurant: Codable { 13 | let name: String 14 | let category: String 15 | let deliveryTime: DeliveryTime 16 | } 17 | 18 | // MARK: - Restaurant + CustomStringConvertible 19 | 20 | extension Restaurant: CustomStringConvertible { 21 | var description: String { 22 | return "\nName: \(name), category: \(category), deliveryTime (min/max): \(deliveryTime.min)/\(deliveryTime.max)" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/RestaurantList/Models/RestaurantsDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantsDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Dairan on 14/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantDetail: Codable { 11 | let name: String 12 | let category: String 13 | let deliveryTime: DeliveryTime 14 | let reviews: Reviews 15 | let menu: [Menu] 16 | } 17 | 18 | // MARK: - DeliveryTime 19 | struct DeliveryTime: Codable { 20 | let min: Int 21 | let max: Int 22 | } 23 | 24 | // MARK: - Menu 25 | struct Menu: Codable { 26 | let category: Category 27 | let name: String 28 | let price: Int 29 | } 30 | 31 | enum Category: String, Codable { 32 | case almoço = "Almoço" 33 | case caféDaManhã = "Café da manhã" 34 | case jantar = "Jantar" 35 | } 36 | 37 | // MARK: - Reviews 38 | struct Reviews: Codable { 39 | let score: Double 40 | let count: Int 41 | } 42 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = RestaurantListView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-1/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | DataSourceUserDefaults.addValue(with: "John Appleseed", forKey: .name) 16 | DataSourceUserDefaults.addValue(with: "john@apple.com", forKey: .email) 17 | DataSourceUserDefaults.addValue(with: "Rua Bela Cintra, 495 - Consolação", forKey: .address) 18 | DataSourceUserDefaults.addValue(with: "Cartão de Crédito", forKey: .payment) 19 | 20 | return true 21 | } 22 | 23 | // MARK: UISceneSession Lifecycle 24 | 25 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 26 | 27 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 28 | } 29 | } 30 | 31 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | self.window?.rootViewController = UINavigationController(rootViewController: HomeViewController()) 21 | self.window?.windowScene = windowScene 22 | self.window?.makeKeyAndVisible() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Murillo R. Araújo on 19/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Decodable { 11 | let street: String 12 | let number: String 13 | let neighborhood: String 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alexandre Cardoso on 09/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Restaurant: Decodable { 11 | let name: String 12 | let category: String 13 | let deliveryTime: DeliveryTime 14 | let reviews: Reviews? 15 | let menu: [MenuItem]? 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case name, category, reviews, menu 19 | case deliveryTime = "delivery_time" 20 | } 21 | } 22 | 23 | struct DeliveryTime: Decodable { 24 | let min: Int 25 | let max: Int 26 | } 27 | 28 | struct Reviews: Decodable { 29 | let score: Double 30 | let count: Int 31 | } 32 | 33 | struct MenuItem: Decodable { 34 | let category: String 35 | let name: String 36 | let price: Double 37 | } 38 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | ServerURL 6 | $(SERVER_URL) 7 | NSAppTransportSecurity 8 | 9 | NSAllowsArbitraryLoads 10 | 11 | 12 | UIApplicationSceneManifest 13 | 14 | UIApplicationSupportsMultipleScenes 15 | 16 | UISceneConfigurations 17 | 18 | UIWindowSceneSessionRoleApplication 19 | 20 | 21 | UISceneConfigurationName 22 | Default Configuration 23 | UISceneDelegateClassName 24 | $(PRODUCT_MODULE_NAME).SceneDelegate 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Murillo R. Araújo on 19/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol AddressSearchPresentable: AnyObject { 11 | func displayAddressSearch(with addresses: [Address]) 12 | func displayErros(error: Error) 13 | } 14 | 15 | class AddressSearchViewModel { 16 | let service: APIManagerProtocol 17 | 18 | weak var presenter: AddressSearchPresentable? 19 | 20 | init(with service: APIManagerProtocol = APIManager()) { 21 | self.service = service 22 | } 23 | 24 | func loadAddressSearch() { 25 | 26 | let apiURl = "https://raw.githubusercontent.com/devpass-tech/challenge-delivery-app/main/api/address_search_results.json" 27 | 28 | service.performRequest(pathURL: apiURl, method: .get) { (result: Result<[Address], APIError>) in 29 | switch result { 30 | case .success(let addresses): 31 | self.presenter?.displayAddressSearch(with: addresses) 32 | 33 | case .failure(let error): 34 | self.presenter?.displayErros(error: error) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Delivery App" 16 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 17 | style: .plain, 18 | target: nil, 19 | action: nil) 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | navigationController?.navigationBar.prefersLargeTitles = true 28 | } 29 | 30 | override func loadView() { 31 | self.view = HomeView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetailsViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Murillo R. Araújo on 11/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RestaurantDetailsPresentable: AnyObject { 11 | func displayRestaurantDetails(with restaurant: Restaurant) 12 | func displayErros(error: Error) 13 | } 14 | 15 | class RestaurantDetailsViewModel { 16 | let service: APIManagerProtocol 17 | 18 | weak var presenter: RestaurantDetailsPresentable? 19 | 20 | init(with service: APIManagerProtocol = APIManager()) { 21 | self.service = service 22 | } 23 | 24 | func loadRestaurantDetails() { 25 | 26 | let apiURl = "https://raw.githubusercontent.com/devpass-tech/challenge-delivery-app/main/api/restaurant_details.json" 27 | 28 | service.performRequest(pathURL: apiURl, method: .get) { (result: Result) in 29 | switch result { 30 | case .success(let restaurant): 31 | self.presenter?.displayRestaurantDetails(with: restaurant) 32 | 33 | case .failure(let error): 34 | self.presenter?.displayErros(error: error) 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = RestaurantListView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Service/APIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIError.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alexandre Cardoso on 09/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum APIError: Error { 11 | case invalidURL 12 | case errorNetwork 13 | case noData 14 | case parseError 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Service/APIManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIManager.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alexandre Cardoso on 09/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum HTTPMethod: String { 11 | case get = "GET" 12 | } 13 | 14 | class APIManager: APIManagerProtocol { 15 | 16 | func performRequest( 17 | pathURL: String, 18 | method: HTTPMethod, 19 | completion: @escaping (Result) -> Void 20 | ) { 21 | 22 | guard let baseURL = Bundle.main.object(forInfoDictionaryKey: "ServerURL") as? String, 23 | let url = URL(string: baseURL + pathURL) 24 | else { return completion(.failure(.invalidURL)) } 25 | 26 | var request = URLRequest(url: url) 27 | request.httpMethod = method.rawValue 28 | 29 | let dataTask = URLSession.shared.dataTask(with: request) { data, response, error in 30 | if let error = error as? APIError { 31 | return completion(.failure(error)) 32 | } 33 | 34 | if let response = response as? HTTPURLResponse, !(200...299 ~= response.statusCode) { 35 | completion(.failure(.errorNetwork)) 36 | return 37 | } 38 | 39 | guard let data = data else { 40 | completion(.failure(.noData)) 41 | return 42 | } 43 | 44 | do { 45 | let decoder = JSONDecoder() 46 | let result = try decoder.decode(T.self, from: data) 47 | completion(.success(result)) 48 | } catch { 49 | completion(.failure(.parseError)) 50 | } 51 | } 52 | 53 | dataTask.resume() 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Service/DataSourceUserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSourceUserDefaults.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alexandre Cardoso on 11/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Keys: String { 11 | case payment = "payment-method" 12 | case email = "user-email" 13 | case name = "user-name" 14 | case address = "address" 15 | } 16 | 17 | class DataSourceUserDefaults { 18 | 19 | static func addValue(with value: Any, forKey: Keys) { 20 | UserDefaults.standard.set(value, forKey: forKey.rawValue) 21 | } 22 | 23 | static func getValue(with forKey: Keys) -> Any? { 24 | return UserDefaults.standard.object(forKey: forKey.rawValue) 25 | } 26 | 27 | static func deleteValue(with forKey: Keys) { 28 | UserDefaults.standard.removeObject(forKey: forKey.rawValue) 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Service/DeliveryApi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApi.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DeliveryApi { 11 | 12 | func fetchRestaurants(_ completion: ([String]) -> Void) { 13 | 14 | completion(["Restaurant 1", "Restaurant 2", "Restaurant 3"]) 15 | } 16 | 17 | func searchAddresses(_ completion: ([String]) -> Void) { 18 | 19 | completion(["Address 1", "Address 2", "Address 3"]) 20 | } 21 | 22 | func fetchRestaurantDetails(_ completion: (String) -> Void) { 23 | 24 | completion("Restaurant Details") 25 | } 26 | 27 | func fetchMenuItem(_ completion: (String) -> Void) { 28 | 29 | completion("Menu Item") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallenge/Service/Protocols/APIManagerProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIManagerProtocol.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alexandre Cardoso on 10/11/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol APIManagerProtocol { 11 | func performRequest(pathURL: String, method: HTTPMethod, completion: @escaping(Result) -> Void) 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/DataSourceUserDefaultsTestCase.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DataSourceUserDefaultsTestCase.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Alexandre Cardoso on 10/11/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DataSourceUserDefaultsTestCase: XCTestCase { 12 | 13 | func testAddValueInUserDefaults() { 14 | let valueExpected = "Test-Add" 15 | DataSourceUserDefaults.addValue(with: valueExpected, forKey: .name) 16 | 17 | XCTAssertEqual(DataSourceUserDefaults.getValue(with: .name) as? String, valueExpected) 18 | makeDeleteTest(forKey: .name) 19 | } 20 | 21 | func testGetValueInUserDefaults() { 22 | let valueExpected = makeAddTest(forKey: .email) 23 | 24 | XCTAssertEqual(DataSourceUserDefaults.getValue(with: .email) as? String, valueExpected) 25 | makeDeleteTest(forKey: .email) 26 | } 27 | 28 | func testDeleteValueInUserDefaults() { 29 | let _ = makeAddTest(forKey: .payment) 30 | DataSourceUserDefaults.deleteValue(with: .payment) 31 | 32 | XCTAssertEqual(DataSourceUserDefaults.getValue(with: .payment) as? String, nil) 33 | } 34 | 35 | } 36 | 37 | extension DataSourceUserDefaultsTestCase { 38 | 39 | func makeDeleteTest(forKey: Keys) { 40 | DataSourceUserDefaults.deleteValue(with: forKey) 41 | } 42 | 43 | func makeAddTest(forKey: Keys) -> String { 44 | let value = "Test Mock" 45 | DataSourceUserDefaults.addValue(with: value, forKey: forKey) 46 | return value 47 | } 48 | 49 | } 50 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/Mock/APIManagerMock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIManagerMock.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Alexandre Cardoso on 09/11/21. 6 | // 7 | 8 | import Foundation 9 | @testable import DeliveryAppChallenge 10 | 11 | class APIManagerMock: APIManagerProtocol { 12 | 13 | var errorAPI: APIError? = nil 14 | 15 | func performRequest( 16 | pathURL: String, 17 | method: HTTPMethod, 18 | completion: @escaping (Result) -> Void 19 | ) { 20 | 21 | switch errorAPI { 22 | case .invalidURL: 23 | completion(.failure(.invalidURL)) 24 | case .parseError: 25 | completion(.failure(.parseError)) 26 | case .noData: 27 | completion(.failure(.noData)) 28 | case .errorNetwork: 29 | completion(.failure(.errorNetwork)) 30 | default: 31 | if let jsonResult = parseJSONMock(), let result = jsonResult as? T { 32 | completion(.success(result)) 33 | } else { 34 | completion(.failure(.parseError)) 35 | } 36 | } 37 | } 38 | 39 | private func parseJSONMock() -> Restaurant? { 40 | 41 | guard let path = Bundle.main.path(forResource: "restaurant_details", ofType: "json") 42 | else { return nil } 43 | 44 | do { 45 | let data = try Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe) 46 | let result = try JSONDecoder().decode(Restaurant.self, from: data) 47 | return result 48 | } catch { 49 | return nil 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/Mock/MockRestaurantDetailsPresentable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockRestaurantDetailsPresentable.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Murillo R. Araújo on 13/11/21. 6 | // 7 | 8 | import Foundation 9 | @testable import DeliveryAppChallenge 10 | 11 | class MockRestaurantDetailsPresentable: RestaurantDetailsPresentable { 12 | 13 | var displayedDetails = false 14 | var displayedErros = false 15 | 16 | func displayRestaurantDetails(with restaurantDetails: Restaurant) { 17 | displayedDetails = true 18 | } 19 | 20 | func displayErros(error: Error) { 21 | displayedErros = true 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/Mock/restaurant_details.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Benjamin A Padaria", 3 | "category": "Padaria", 4 | "delivery_time": { 5 | "min": 23, 6 | "max": 33 7 | }, 8 | "reviews": { 9 | "score": 4.8, 10 | "count": 351 11 | }, 12 | "menu": [ 13 | { 14 | "category": "Café da manhã", 15 | "name": "Copo Pão de Queijo + Suco de Laranja", 16 | "price": 20 17 | }, 18 | { 19 | "category": "Café da manhã", 20 | "name": "Iogurte granola e mel", 21 | "price": 12 22 | } 23 | ] 24 | } 25 | -------------------------------------------------------------------------------- /solutions/devsprint-2/DeliveryAppChallengeTests/RestaurantDetailsViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetailsViewModelTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Murillo R. Araújo on 13/11/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class RestaurantDetailsViewModelTests: XCTestCase { 12 | 13 | var sut: RestaurantDetailsViewModel! 14 | let presenter = MockRestaurantDetailsPresentable() 15 | let service = APIManagerMock() 16 | 17 | override func setUp() { 18 | sut = RestaurantDetailsViewModel(with: service) 19 | sut.presenter = presenter 20 | super.setUp() 21 | } 22 | 23 | override func tearDown() { 24 | super.tearDown() 25 | sut = nil 26 | } 27 | 28 | func testLoadRestaurantDetails() throws { 29 | sut.loadRestaurantDetails() 30 | XCTAssertTrue(presenter.displayedDetails) 31 | } 32 | 33 | func testLoadRestaurantDetailsWithError() throws { 34 | service.errorAPI = .noData 35 | sut.loadRestaurantDetails() 36 | XCTAssertTrue(presenter.displayedErros) 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/AppDelegate/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 21/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class AppCoordinator: Coordinator { 11 | 12 | // MARK: - Properties 13 | var navigationController: UINavigationController 14 | var childCoordinator: [Coordinator] = [] 15 | var parentCoordinator: Coordinator? 16 | 17 | // MARK: - Super Methods 18 | init() { 19 | navigationController = UINavigationController() 20 | } 21 | 22 | // MARK: Methods 23 | func star() { 24 | let childCoordinator = HomeViewCoordinator(navigationController: navigationController) 25 | childCoordinator.parentCoordinator = self 26 | add(childCoordinator: childCoordinator) 27 | childCoordinator.star() 28 | } 29 | 30 | func childDidFinish(_ child: Coordinator) { 31 | parentCoordinator?.childDidFinish(self) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | var appCoodinator: AppCoordinator? 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(windowScene: windowScene) 20 | appCoodinator = AppCoordinator() 21 | appCoodinator?.star() 22 | window?.rootViewController = appCoodinator?.navigationController 23 | window?.makeKeyAndVisible() 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Coordinator/Coordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 21/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol Coordinator: AnyObject { 11 | 12 | var navigationController: UINavigationController { get set } 13 | var childCoordinator: [Coordinator] {get set} 14 | var parentCoordinator: Coordinator? { get set } 15 | 16 | func star() 17 | func add(childCoordinator coordinator: Coordinator) 18 | func remove(childCoordinator coordinator: Coordinator) 19 | func back() 20 | func childDidFinish(_ child: Coordinator) 21 | } 22 | 23 | extension Coordinator { 24 | 25 | // MARK: Methods 26 | func add(childCoordinator coordinator: Coordinator) { 27 | childCoordinator.append(coordinator) 28 | } 29 | 30 | func remove(childCoordinator coordinator: Coordinator) { 31 | childCoordinator = childCoordinator.filter({$0 !== coordinator}) 32 | } 33 | 34 | func back() {} 35 | 36 | func childDidFinish(_ child: Coordinator?) { 37 | guard let child = child else {return} 38 | remove(childCoordinator: child) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | BaseUrl 6 | $(SERVER_URL) 7 | NSAppTransportSecurity 8 | 9 | NSAllowsArbitraryLoads 10 | 11 | 12 | UIApplicationSceneManifest 13 | 14 | UIApplicationSupportsMultipleScenes 15 | 16 | UISceneConfigurations 17 | 18 | UIWindowSceneSessionRoleApplication 19 | 20 | 21 | UISceneConfigurationName 22 | Default Configuration 23 | UISceneDelegateClassName 24 | $(PRODUCT_MODULE_NAME).SceneDelegate 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/MenuListView/Model/Menu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Menu.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Francischett Occhiuto on 21/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Menu: Codable { 11 | let category: String 12 | let name: String 13 | let price: Double 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/MenuListView/Model/RestauranteMenuModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Francischett Occhiuto on 21/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantMenuModel: Codable { 11 | let menu: Menu? 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/MenuListView/ViewModel/MenuViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Francischett Occhiuto on 21/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/RestaurantListView/Coordinator/RestaurantListCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 21/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListCoordinator { 11 | // MARK: - Properties 12 | var navigationController: UINavigationController 13 | var childCoordinator: [Coordinator] = [] 14 | var parentCoordinator: Coordinator? 15 | 16 | // MARK: - Super Methods 17 | init(navigationController: UINavigationController) { 18 | self.navigationController = navigationController 19 | navigationController.navigationBar.isHidden = false 20 | } 21 | 22 | // MARK: Methods 23 | func star() { 24 | let vc = HomeViewController() 25 | navigationController.pushViewController(vc, animated: true) 26 | } 27 | 28 | func goTableView() { 29 | let vc = RestaurantDetailsViewController() 30 | navigationController.pushViewController(vc, animated: true) 31 | } 32 | 33 | func childDidFinish(_ child: Coordinator) { 34 | // parentCoordinator?.childDidFinish(self) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/RestaurantListView/Model/DeliveryTime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryTime.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 16/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DeliveryTime: Codable { 11 | let min: Int? 12 | let max: Int? 13 | } 14 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/RestaurantListView/Model/RestaurantModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 14/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantModel: Codable { 11 | let name: String? 12 | let category: String? 13 | let deliveryTime: DeliveryTime? 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case name, category 17 | case deliveryTime = "delivery_time" 18 | } 19 | } 20 | 21 | 22 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Components/RestaurantListView/ViewModel/RestaurantListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 16/02/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RestuarantListPresentable: AnyObject { 11 | func reloadData() 12 | func presentError(title: String, message: APIError) 13 | } 14 | 15 | class RestaurantListViewModel { 16 | 17 | let delegate: DeliveryApiProtocol? = DeliveryApi() 18 | var delegatePresentable: RestuarantListPresentable? = nil 19 | var model: [RestaurantModel]? 20 | 21 | func load() { 22 | delegate?.fetchRestaurants({ model in 23 | switch model { 24 | case .success(let restaurant): 25 | self.model = restaurant 26 | self.delegatePresentable?.reloadData() 27 | case .failure(let error): 28 | self.delegatePresentable?.presentError(title: "Algo deu errado", message: error) 29 | } 30 | }) 31 | } 32 | 33 | func numberOfRow()-> Int { 34 | return model?.count ?? 0 35 | } 36 | 37 | func cellForRow(indexPath: IndexPath) -> RestaurantModel { 38 | return model?[indexPath.row] ?? RestaurantModel(name: "", category: "", deliveryTime: DeliveryTime(min: 0, max: 0)) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Home/Coordinator/HomeViewCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Tatiana Rico on 21/02/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewCoordinator: Coordinator { 11 | var navigationController: UINavigationController 12 | var childCoordinator: [Coordinator] = [] 13 | var parentCoordinator: Coordinator? 14 | 15 | // MARK: - Super Methods 16 | init(navigationController: UINavigationController) { 17 | self.navigationController = navigationController 18 | navigationController.navigationBar.isHidden = false 19 | } 20 | 21 | // MARK: Methods 22 | func star() { 23 | let vc = HomeViewController() 24 | navigationController.pushViewController(vc, animated: true) 25 | } 26 | 27 | func childDidFinish(_ child: Coordinator) { 28 | parentCoordinator?.childDidFinish(self) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Delivery App" 16 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 17 | style: .plain, 18 | target: nil, 19 | action: nil) 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | navigationController?.navigationBar.prefersLargeTitles = true 28 | } 29 | 30 | override func loadView() { 31 | let homeView = HomeView() 32 | homeView.delegate = self 33 | self.view = homeView 34 | } 35 | } 36 | 37 | extension HomeViewController: HomeViewDelegate { 38 | func goToDetailListRestaurant() { 39 | guard let navigation = self.navigationController else { return } 40 | let delegate: RestaurantListCoordinator = RestaurantListCoordinator(navigationController: navigation) 41 | delegate.goTableView() 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | var listViewDelegate: RestaurantListViewDelegate? 12 | 13 | init() { 14 | super.init(nibName: nil, bundle: nil) 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func viewDidLoad() { 23 | self.listViewDelegate = self 24 | } 25 | 26 | override func loadView() { 27 | self.view = RestaurantListView() 28 | } 29 | } 30 | 31 | 32 | extension RestaurantListViewController: RestaurantListViewDelegate { 33 | func pushViewController() { 34 | guard let navigation = self.navigationController else { return } 35 | let delegate: RestaurantListCoordinator = RestaurantListCoordinator(navigationController: navigation) 36 | delegate.goTableView() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallenge/Service/DeliveryApi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApi.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DeliveryApiProtocol { 11 | func fetchRestaurants(_ completion: @escaping (Result<[RestaurantModel]?, APIError>) -> Void) 12 | func fetchMenuItem(_ completion: @escaping (Result<[RestaurantMenuModel]?, APIError>) -> Void) 13 | } 14 | 15 | struct DeliveryApi: DeliveryApiProtocol { 16 | 17 | let delegate: APIManagerProtocol = ApiManager() 18 | 19 | func fetchRestaurants(_ completion: @escaping (Result<[RestaurantModel]?, APIError>) -> Void) { 20 | let pathUrl = "/devpass-tech/challenge-delivery-app/main/api/home_restaurant_list.json" 21 | delegate.performRequest(pathURL: pathUrl, method: .get, completion: completion) 22 | } 23 | 24 | func searchAddresses(_ completion: ([String]) -> Void) { 25 | 26 | completion(["Address 1", "Address 2", "Address 3"]) 27 | } 28 | 29 | func fetchRestaurantDetails(_ completion: (String) -> Void) { 30 | 31 | completion("Restaurant Details") 32 | } 33 | 34 | func fetchMenuItem(_ completion: @escaping (Result<[RestaurantMenuModel]?, APIError>) -> Void) { 35 | let pathUrl = "/devpass-tech/challenge-delivery-app/main/api/restaurant_details.json" 36 | delegate.performRequest(pathURL: pathUrl, method: .get, completion: completion) 37 | } 38 | 39 | ///devpass-tech/challenge-delivery-app/main/api/restaurant_details.json 40 | } 41 | -------------------------------------------------------------------------------- /solutions/devsprint-maria-eugenia-1/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | let viewModel = HomeViewModel(service: DeliveryApi()) 21 | self.window?.rootViewController = UINavigationController(rootViewController: HomeViewController(viewModel: viewModel)) 22 | self.window?.windowScene = windowScene 23 | self.window?.makeKeyAndVisible() 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Coordinator/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 24/05/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class AppCoordinator { 11 | 12 | var childCoordinators: [Coordinator] = [] 13 | var presenter: UINavigationController 14 | 15 | init(presenter: UINavigationController) { 16 | self.presenter = presenter 17 | } 18 | 19 | func start() { 20 | let child = HomeCoordinator(presenter: presenter) 21 | childCoordinators.append(child) 22 | child.parentCoordinator = self 23 | child.start() 24 | } 25 | 26 | func childDidFinish(_ child: Coordinator?) { 27 | for (index, coordinator) in childCoordinators.enumerated() { 28 | if coordinator === child { 29 | childCoordinators.remove(at: index) 30 | } 31 | } 32 | } 33 | } 34 | 35 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Coordinator/Coordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 24/05/22. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol Coordinator: AnyObject { 11 | 12 | var presenter: UINavigationController { get set } 13 | var parentCoordinator: AppCoordinator? { get set } 14 | 15 | func start() 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Coordinator/HomeCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 24/05/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class HomeCoordinator: Coordinator { 11 | 12 | var presenter: UINavigationController 13 | weak var parentCoordinator: AppCoordinator? 14 | 15 | init(presenter: UINavigationController) { 16 | self.presenter = presenter 17 | } 18 | 19 | func start() { 20 | let viewModel = HomeViewModel(service: DeliveryApi()) 21 | viewModel.coordinator = self 22 | let viewController = HomeViewController(viewModel: viewModel) 23 | presenter.pushViewController(viewController, animated: true) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Adress.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Vieira Souza on 16/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Codable { 11 | let street: String 12 | let number: String 13 | let neighborhood: String 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Models/MenuItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Luiza Moruz on 18/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MenuItem: Codable { 11 | let name: String 12 | let description: String 13 | let price: Float 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 11/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Restaurant: Codable { 11 | let name, category: String 12 | let deliveryTime: DeliveryTime 13 | 14 | func formattedInfo() -> String { 15 | return "\(category) • \(deliveryTime.min)-\(deliveryTime.max) min" 16 | } 17 | } 18 | 19 | struct DeliveryTime: Codable { 20 | let min, max: Int 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Models/RestaurantDetail.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetail.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by pedro tres on 25/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantDetail: Codable { 11 | let name, category: String 12 | let deliveryTime: DeliveryTime 13 | let reviews: Reviews 14 | let menu: [MenuItem] 15 | } 16 | 17 | struct Reviews: Codable { 18 | let score: Double 19 | let count: Int 20 | } 21 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | APPL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | NSAppTransportSecurity 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | $(PRODUCT_MODULE_NAME).SceneDelegate 36 | 37 | 38 | 39 | 40 | UILaunchStoryboardName 41 | LaunchScreen 42 | UISupportedInterfaceOrientations 43 | 44 | 45 | 46 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Vieira Souza on 18/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class AddressSearchViewModel { 11 | 12 | private var service: DeliveryApi 13 | 14 | private var addresses: [Address] = [] 15 | 16 | init(service: DeliveryApi) { 17 | self.service = service 18 | } 19 | 20 | func getAddressesList(completion: @escaping () -> Void) { 21 | service.searchAddresses { result in 22 | switch result { 23 | case .success(let addresses): 24 | self.addresses = addresses 25 | completion() 26 | case .failure(let error): 27 | print(error) 28 | } 29 | } 30 | } 31 | } 32 | 33 | extension AddressSearchViewModel: AddressesListViewDataSource { 34 | 35 | func getItemCount() -> Int { 36 | return addresses.count 37 | } 38 | 39 | func getData(at: Int) -> Address { 40 | return addresses[at] 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | private let viewModel: HomeViewModel 13 | 14 | init(viewModel: HomeViewModel) { 15 | self.viewModel = viewModel 16 | super.init(nibName: nil, bundle: nil) 17 | 18 | navigationItem.title = "Delivery App 🍕" 19 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 20 | style: .plain, 21 | target: nil, 22 | action: nil) 23 | } 24 | 25 | required init?(coder: NSCoder) { 26 | fatalError("init(coder:) has not been implemented") 27 | } 28 | 29 | override func viewDidLoad() { 30 | navigationController?.navigationBar.prefersLargeTitles = true 31 | } 32 | 33 | override func loadView() { 34 | let homeView = HomeView() 35 | self.view = homeView 36 | 37 | homeView.restaurantListView.dataSource = viewModel 38 | 39 | viewModel.getRestaurantList { 40 | DispatchQueue.main.async { 41 | homeView.restaurantListView.tableView.reloadData() 42 | } 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/Home/HomeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by pedro tres on 18/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | final class HomeViewModel { 11 | 12 | private let service: DeliveryApiProtocol 13 | var coordinator: HomeCoordinator? 14 | 15 | public var restaurants: [Restaurant] = [] 16 | 17 | init(service: DeliveryApiProtocol){ 18 | self.service = service 19 | } 20 | 21 | func getRestaurantList(completion: @escaping () -> Void) { 22 | service.fetchRestaurants { (resut: Result<[Restaurant], DeliveryApiError>) in 23 | switch resut { 24 | case .success(let restaurants): 25 | self.restaurants = restaurants 26 | completion() 27 | case .failure(let error): 28 | print(error) 29 | } 30 | } 31 | } 32 | } 33 | 34 | extension HomeViewModel: RestaurantListViewDataSource { 35 | func getData(at: Int) -> Restaurant { 36 | return restaurants[at] 37 | } 38 | 39 | func getItemCount() -> Int { 40 | return restaurants.count 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = RestaurantListView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Screens/Settings/SettingsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Anderson Oliveira on 18/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | final class SettingsViewModel { 11 | 12 | enum UserInfoData: String { 13 | case userName = "user-name" 14 | case userEmail = "user-email" 15 | case userAddress = "address" 16 | case paymentMethod = "payment-method" 17 | } 18 | 19 | func getInfo(for key: UserInfoData) -> String { 20 | return UserInfo.getData(key: key.rawValue) 21 | } 22 | 23 | } 24 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Service/DeliveryApiError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApiError.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by pedro tres on 17/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum DeliveryApiError: Error { 11 | case invalidURL 12 | case requestError(description: String) 13 | case invalidResponse 14 | case invalidData 15 | case decodingError(description: String) 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallenge/Service/UserInfo.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserInfo.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Anderson Oliveira on 18/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | 11 | struct UserInfo { 12 | 13 | static func getData(key: String) -> String { 14 | return UserDefaults.standard.string(forKey: key) ?? "" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallengeTests/HomeViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModelTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by pedro tres on 23/05/22. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class HomeViewModelTests: XCTestCase { 12 | 13 | var sut: HomeViewModel! 14 | let service = DeliveryApiMock() 15 | 16 | 17 | override func setUp() { 18 | sut = HomeViewModel(service: service) 19 | super.setUp() 20 | } 21 | 22 | override func tearDown() { 23 | sut = nil 24 | super.tearDown() 25 | } 26 | 27 | func test_fetchRestaurants_withData_shouldCallWithSuccess() { 28 | sut.getRestaurantList{ 29 | XCTAssertFalse(self.sut.restaurants.isEmpty) 30 | } 31 | 32 | } 33 | 34 | func test_fetchRestaurants_invalidData_shouldCallWithInvalidData() { 35 | service.error = .invalidData 36 | sut.getRestaurantList{ 37 | XCTAssert(self.sut.restaurants.isEmpty) 38 | } 39 | } 40 | 41 | func test_getItemCount_shouldReturnRestaurantLength () { 42 | sut.restaurants.append(Restaurant(name: "Giraffas", category: "fast-food", deliveryTime: DeliveryTime(min: 10, max: 25))) 43 | XCTAssertEqual(sut.getItemCount(), 1) 44 | } 45 | 46 | func test_getData_sholdReturnRestaurant() { 47 | sut.restaurants.append(Restaurant(name: "Giraffas", category: "fast-food", deliveryTime: DeliveryTime(min: 10, max: 25))) 48 | XCTAssertEqual(sut.getData(at: 0).name, "Giraffas") 49 | } 50 | 51 | } 52 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallengeTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | BNDL 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/DeliveryAppChallengeTests/Mock/DeliveryApiMock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApiMock.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by pedro tres on 23/05/22. 6 | // 7 | 8 | import Foundation 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryApiMock: DeliveryApiProtocol { 12 | var error: DeliveryApiError? 13 | 14 | func fetchRestaurants(_ completion: @escaping (Result<[Restaurant], DeliveryApiError>) -> Void) { 15 | switch error { 16 | case .invalidURL: 17 | completion(.failure(.invalidURL)) 18 | case .requestError: 19 | completion(.failure(.requestError(description: "request error"))) 20 | case .invalidResponse: 21 | completion(.failure(.invalidResponse)) 22 | case .invalidData: 23 | completion(.failure(.invalidData)) 24 | case .decodingError: 25 | completion(.failure(.decodingError(description: "deconding error"))) 26 | default: 27 | completion(.success([Restaurant(name: "Giraffas", category: "fast-food", deliveryTime: DeliveryTime(min: 10, max: 25))])) 28 | } 29 | } 30 | 31 | func searchAddresses(_ completion: @escaping (Result<[Address], DeliveryApiError>) -> Void) { 32 | 33 | } 34 | 35 | func fetchRestaurantDetails(_ completion: (String) -> Void) { 36 | 37 | } 38 | 39 | func fetchMenuItem(_ completion: (String) -> Void) { 40 | 41 | } 42 | 43 | 44 | } 45 | 46 | 47 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/Podfile: -------------------------------------------------------------------------------- 1 | # Uncomment the next line to define a global platform for your project 2 | # platform :ios, '9.0' 3 | 4 | target 'DeliveryAppChallenge' do 5 | # Comment the next line if you don't want to use dynamic frameworks 6 | use_frameworks! 7 | 8 | # Pods for DeliveryAppChallenge 9 | 10 | target 'DeliveryAppChallengeTests' do 11 | inherit! :search_paths 12 | # Pods for testing 13 | end 14 | 15 | end 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/Podfile.lock: -------------------------------------------------------------------------------- 1 | PODFILE CHECKSUM: f6d4c8772b0ad0092b8ea9fd365eaa58fc4d23ab 2 | 3 | COCOAPODS: 1.10.2 4 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-2/project.yml: -------------------------------------------------------------------------------- 1 | name: DeliveryAppChallenge 2 | options: 3 | bundleIdPrefix: com.devpass 4 | deploymentTarget: 5 | iOS: 15.0 6 | postGenCommand: pod install 7 | 8 | targets: 9 | DeliveryAppChallenge: 10 | type: application 11 | platform: iOS 12 | sources: 13 | - DeliveryAppChallenge 14 | scheme: 15 | testTargets: 16 | - DeliveryAppChallengeTests 17 | 18 | info: 19 | path: DeliveryAppChallenge/Resources/Info.plist 20 | properties: 21 | UISupportedInterfaceOrientations: [] 22 | NSAppTransportSecurity: true 23 | UILaunchStoryboardName: LaunchScreen 24 | UIApplicationSceneManifest: 25 | UIApplicationSupportsMultipleScenes: false 26 | UISceneConfigurations: 27 | UIWindowSceneSessionRoleApplication: 28 | - UISceneConfigurationName: Default Configuration 29 | UISceneDelegateClassName: $(PRODUCT_MODULE_NAME).SceneDelegate 30 | 31 | DeliveryAppChallengeTests: 32 | type: bundle.unit-test 33 | platform: iOS 34 | sources: 35 | - path: DeliveryAppChallengeTests 36 | includes: 37 | - "**/*.swift" 38 | 39 | settings: 40 | TEST_HOST: "$(BUILT_PRODUCTS_DIR)/DeliveryAppChallenge.app/DeliveryAppChallenge" 41 | 42 | info: 43 | path: DeliveryAppChallengeTests/Info.plist 44 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | 21 | let viewModel = RestaurantListViewModel(service: DeliveryApi()) 22 | let viewController = RestaurantListViewController(viewModel: viewModel) 23 | 24 | self.window?.rootViewController = UINavigationController(rootViewController: viewController) 25 | self.window?.windowScene = windowScene 26 | self.window?.makeKeyAndVisible() 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Vitor Conceicao on 10/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Codable { 11 | let street: String 12 | let number: String 13 | let neighborhood: String 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Models/MenuItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Menu.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Vitor Conceicao on 10/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MenuItem: Codable { 11 | let name: String 12 | let description: String 13 | let price: Int 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 11/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DeliveryTime: Decodable { 11 | var min: Int 12 | var max: Int 13 | } 14 | 15 | struct Restaurant: Decodable { 16 | var name: String 17 | var category: String 18 | var deliveryTime: DeliveryTime 19 | 20 | enum CodingKeys: String, CodingKey { 21 | case name,category 22 | case deliveryTime = "delivery_time" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | DeliveryApiBaseUrl 11 | https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/main/api 12 | UIApplicationSceneManifest 13 | 14 | UIApplicationSupportsMultipleScenes 15 | 16 | UISceneConfigurations 17 | 18 | UIWindowSceneSessionRoleApplication 19 | 20 | 21 | UISceneConfigurationName 22 | Default Configuration 23 | UISceneDelegateClassName 24 | $(PRODUCT_MODULE_NAME).SceneDelegate 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Delivery App 🍕" 16 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 17 | style: .plain, 18 | target: nil, 19 | action: nil) 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | navigationController?.navigationBar.prefersLargeTitles = true 28 | } 29 | 30 | override func loadView() { 31 | self.view = HomeView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | private lazy var customView: RestaurantListView = { 13 | let view = RestaurantListView() 14 | view.dataSource = viewModel 15 | return view 16 | }() 17 | 18 | private let viewModel: RestaurantListViewModel 19 | 20 | init(viewModel: RestaurantListViewModel) { 21 | self.viewModel = viewModel 22 | 23 | super.init(nibName: nil, bundle: nil) 24 | 25 | navigationItem.title = "Restaurant List" 26 | viewModel.presenter = self 27 | } 28 | 29 | required init?(coder: NSCoder) { 30 | fatalError("init(coder:) has not been implemented") 31 | } 32 | 33 | override func loadView() { 34 | self.view = customView 35 | } 36 | 37 | override func viewDidLoad() { 38 | super.viewDidLoad() 39 | 40 | viewModel.fetchRestaurants() 41 | } 42 | } 43 | 44 | extension RestaurantListViewController: RestaurantListViewPresentable { 45 | func reloadList() { 46 | DispatchQueue.main.async { 47 | self.customView.tableView.reloadData() 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 04/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RestaurantListViewPresentable: AnyObject { 11 | func reloadList() 12 | } 13 | 14 | final class RestaurantListViewModel { 15 | 16 | private let service: DeliveryApiProtocol 17 | weak var presenter: RestaurantListViewPresentable? 18 | 19 | public var restaurants: [Restaurant] = [] 20 | 21 | init(service: DeliveryApiProtocol) { 22 | self.service = service 23 | } 24 | 25 | public func fetchRestaurants() { 26 | service.fetchRestaurants { result in 27 | switch result { 28 | case .success(let restaurants): 29 | self.restaurants = restaurants 30 | self.presenter?.reloadList() 31 | case .failure(let error): 32 | print(error) 33 | } 34 | } 35 | } 36 | } 37 | 38 | extension RestaurantListViewModel: RestaurantListViewDataSource { 39 | func getItem(at index: Int) -> Restaurant { 40 | return restaurants[index] 41 | } 42 | 43 | func getItemCount() -> Int { 44 | return restaurants.count 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Service/Delivery/DeliveryApiProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApiProtocol.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 01/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol DeliveryApiProtocol { 11 | var networkManager: NetworkManager { get } 12 | 13 | func fetchRestaurants(completion: @escaping ((Result<[Restaurant],ServiceError>) -> Void)) 14 | func fetchMenuItem(completion: @escaping (Result) -> Void) 15 | func fetchSearchAddresses(completion: @escaping (Result<[Address], ServiceError>) -> Void) 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Service/Delivery/DeliveryApiRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApiRequest.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 02/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum DeliveryApiRequest { 11 | case fetchRestaurants 12 | case fetchRestaurantDetail 13 | case fetchSearchAdresses 14 | case fetchMenuItem 15 | 16 | static let baseUrl: String = { 17 | let urlEnv = Bundle.main.object(forInfoDictionaryKey: "DeliveryApiBaseUrl") 18 | return (urlEnv as? String) ?? "" 19 | }() 20 | 21 | private var url: URL { URL(string: Self.baseUrl+path)! } 22 | 23 | var path: String { 24 | switch self { 25 | case .fetchRestaurants: 26 | return "/home_restaurant_list.json" 27 | case .fetchRestaurantDetail: 28 | return "/restaurant_details.json" 29 | case .fetchSearchAdresses: 30 | return "/address_search_results.json" 31 | case .fetchMenuItem: 32 | return "/menu_item_details.json" 33 | } 34 | } 35 | 36 | var urlRequest: URLRequest { 37 | var req = URLRequest(url: url) 38 | req.httpMethod = "GET" 39 | return req 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Service/Networking/NetworkManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkManager.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 01/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NetworkManager { 11 | var session: URLSession { get } 12 | 13 | func get(_ request: URLRequest, completion: @escaping ((Result) -> Void)) 14 | } 15 | 16 | final class NetworkManagerService: NetworkManager { 17 | 18 | let session: URLSession 19 | 20 | private var dataTask: URLSessionDataTask? 21 | 22 | init(session: URLSession = .shared) { 23 | self.session = session 24 | } 25 | 26 | deinit { 27 | dataTask?.cancel() 28 | } 29 | 30 | func get(_ request: URLRequest, completion: @escaping ((Result) -> Void)) { 31 | 32 | dataTask = session.dataTask(with: request) { (data,response,error) in 33 | 34 | if let error = error { 35 | completion(.failure(.requestFailure(error.localizedDescription))) 36 | return 37 | } 38 | 39 | guard let data = data, !data.isEmpty else { 40 | completion(.failure(.emptyData)) 41 | return 42 | } 43 | 44 | let decoder = JSONDecoder() 45 | 46 | guard let result = try? decoder.decode(T.self, from: data) else { 47 | completion(.failure(.decodingError)) 48 | return 49 | } 50 | 51 | completion(.success(result)) 52 | } 53 | 54 | dataTask?.resume() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallenge/Service/Networking/ServiceError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceError.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Matheus dos Reis de Jesus on 01/08/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ServiceError: Error { 11 | case requestFailure(_ description: String) 12 | case emptyData 13 | case decodingError 14 | 15 | var localizedDescription: String { 16 | switch self { 17 | case .requestFailure(let description): 18 | return "The request has failed: \(description)" 19 | case .emptyData: 20 | return "The request has returned empty data" 21 | case .decodingError: 22 | return "The request data couldn't be decoded" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-3/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | self.window?.rootViewController = UINavigationController(rootViewController: HomeViewController()) 21 | self.window?.windowScene = windowScene 22 | self.window?.makeKeyAndVisible() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Decodable { 11 | let street: String 12 | let number: String 13 | let neighborhood: String 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Models/MenuItemDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MenuItemDetails: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 11/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Restaurant: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Models/RestaurantDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantDetails: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIApplicationSceneManifest 11 | 12 | UIApplicationSupportsMultipleScenes 13 | 14 | UISceneConfigurations 15 | 16 | UIWindowSceneSessionRoleApplication 17 | 18 | 19 | UISceneConfigurationName 20 | Default Configuration 21 | UISceneDelegateClassName 22 | $(PRODUCT_MODULE_NAME).SceneDelegate 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Nascimento Marques on 26/01/23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol AddressSearchViewModelProtocol { 11 | var addresses: [Address] { get } 12 | func fetch(completion: @escaping () -> ()) 13 | } 14 | 15 | final class AddressSearchViewModel: AddressSearchViewModelProtocol { 16 | 17 | private var service: DeliveryApi 18 | 19 | var addresses: [Address] = [] 20 | 21 | init(service: DeliveryApi) { 22 | self.service = service 23 | } 24 | 25 | func fetch(completion: @escaping () -> ()) { 26 | service.searchAddresses { (address) in 27 | self.addresses = address 28 | completion() 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Delivery App 🍕" 16 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 17 | style: .plain, 18 | target: nil, 19 | action: nil) 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | navigationController?.navigationBar.prefersLargeTitles = true 28 | } 29 | 30 | override func loadView() { 31 | self.view = HomeView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = RestaurantListView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Service/DeliveryApi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApi.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | class DeliveryApi { 11 | private let networkManager: NetworkManagerProtocol 12 | 13 | init(networkManager: NetworkManagerProtocol) { 14 | self.networkManager = networkManager 15 | } 16 | 17 | func fetchRestaurants(_ completion: ([Restaurant]) -> Void) { 18 | 19 | completion([]) 20 | } 21 | 22 | func searchAddresses(_ completion: @escaping ([Address]) -> Void) { 23 | networkManager.get(DeliveryRequest.addresses) { (result: Result<[Address], NetworkError>) in 24 | switch result { 25 | case .success(let address): 26 | completion(address) 27 | case .failure(let error): 28 | print(error) 29 | } 30 | } 31 | } 32 | 33 | func fetchRestaurantDetails(_ completion: (RestaurantDetails) -> Void) { 34 | 35 | completion(RestaurantDetails()) 36 | } 37 | 38 | func fetchMenuItem(_ completion: (MenuItemDetails) -> Void) { 39 | 40 | completion(MenuItemDetails()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Service/DeliveryRequest.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryRequest.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Nascimento Marques on 25/01/23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum DeliveryRequest: RequestProtocol { 11 | case restaurants 12 | case addresses 13 | case menuItem 14 | case restaurantDetail 15 | 16 | private static var basePath: String { 17 | return "https://raw.githubusercontent.com/Bruques/challenge-mvvm-delivery/main/api" 18 | } 19 | 20 | var path: String { 21 | switch self { 22 | case .restaurants: 23 | return "/home_restaurant_list.json" 24 | case .addresses: 25 | return "/address_search_results.json" 26 | case .menuItem: 27 | return "/menu_item_details.json" 28 | case .restaurantDetail: 29 | return "/restaurant_details.json" 30 | } 31 | } 32 | 33 | var method: HttpMethod { 34 | return .GET 35 | } 36 | 37 | var urlRequest: URLRequest { 38 | let url = URL(string: "\(Self.basePath)\(self.path)")! 39 | 40 | var request = URLRequest(url: url) 41 | request.httpMethod = self.method.rawValue 42 | return request 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Service/NetworkError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkError.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Nascimento Marques on 25/01/23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum NetworkError: Error { 11 | case requestFailure(Error) 12 | case emptyData 13 | case emptyResponse 14 | case decodeFailure 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Service/NetworkManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkManager.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Nascimento Marques on 25/01/23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NetworkManagerProtocol { 11 | func get(_ request: RequestProtocol, completion: @escaping (Result) -> ()) 12 | } 13 | 14 | class NetworkManager: NetworkManagerProtocol { 15 | 16 | private let session: URLSession 17 | 18 | init(session: URLSession = .shared) { 19 | self.session = session 20 | } 21 | 22 | func get(_ request: RequestProtocol, completion: @escaping (Result) -> ()) { 23 | 24 | session.dataTask(with: request.urlRequest) { (data, response, error) in 25 | if let error = error { 26 | completion(.failure(.requestFailure(error))) 27 | return 28 | } 29 | 30 | if response == nil { 31 | completion(.failure(.emptyResponse)) 32 | return 33 | } 34 | 35 | guard let safeData = data else { 36 | completion(.failure(.emptyData)) 37 | return 38 | } 39 | 40 | let decoder = JSONDecoder() 41 | decoder.keyDecodingStrategy = .convertFromSnakeCase 42 | 43 | do { 44 | let decodedData = try decoder.decode(T.self, from: safeData) 45 | completion(.success(decodedData)) 46 | } catch { 47 | completion(.failure(.decodeFailure)) 48 | } 49 | 50 | }.resume() 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallenge/Service/RequestProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RequestProtocol.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Bruno Nascimento Marques on 25/01/23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RequestProtocol { 11 | var path: String { get } 12 | var method: HttpMethod { get } 13 | var urlRequest: URLRequest { get } 14 | } 15 | 16 | enum HttpMethod: String { 17 | case GET 18 | case POST 19 | case PATCH 20 | case DELETE 21 | case UPDATE 22 | } 23 | -------------------------------------------------------------------------------- /solutions/devsprint-matheus-reis-6/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | let userDefaults = UserDefaults.standard 16 | userDefaults.setValue("John Appleseed", forKey: "user-name") 17 | userDefaults.setValue("john@apple.com", forKey: "user-email") 18 | userDefaults.setValue("Rua Bela Cintra, 495 - Consolação", forKey: "address") 19 | userDefaults.setValue("Cartão de Crédito", forKey: "payment-method") 20 | userDefaults.synchronize() 21 | 22 | return true 23 | } 24 | 25 | // MARK: UISceneSession Lifecycle 26 | 27 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 28 | 29 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | 19 | self.window = UIWindow(frame: UIScreen.main.bounds) 20 | self.window?.rootViewController = UINavigationController(rootViewController: SettingsViewController()) 21 | self.window?.windowScene = windowScene 22 | self.window?.makeKeyAndVisible() 23 | } 24 | } 25 | 26 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Models/MenuItemDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MenuItemDetails: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 11/05/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Restaurant: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Models/RestaurantDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 21/09/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantDetails: Decodable { 11 | 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2-1.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 2.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 3.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 4.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy 5.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/AppIcon.appiconset/devpass-logo-blue copy.png -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIApplicationSceneManifest 11 | 12 | UIApplicationSupportsMultipleScenes 13 | 14 | UISceneConfigurations 15 | 16 | UIWindowSceneSessionRoleApplication 17 | 18 | 19 | UISceneConfigurationName 20 | Default Configuration 21 | UISceneDelegateClassName 22 | $(PRODUCT_MODULE_NAME).SceneDelegate 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Delivery App 🍕" 16 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 17 | style: .plain, 18 | target: nil, 19 | action: nil) 20 | } 21 | 22 | required init?(coder: NSCoder) { 23 | fatalError("init(coder:) has not been implemented") 24 | } 25 | 26 | override func viewDidLoad() { 27 | navigationController?.navigationBar.prefersLargeTitles = true 28 | } 29 | 30 | override func loadView() { 31 | self.view = HomeView() 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | print("") 20 | fatalError("init(coder:) has not been implemented") 21 | } 22 | 23 | override func loadView() { 24 | self.view = RestaurantListView() 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Settings" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = SettingsView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallenge/Service/DeliveryApi.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApi.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DeliveryApi { 11 | 12 | func fetchRestaurants(_ completion: ([Restaurant]) -> Void) { 13 | 14 | completion([]) 15 | } 16 | 17 | func searchAddresses(_ completion: ([Address]) -> Void) { 18 | 19 | completion([]) 20 | } 21 | 22 | func fetchRestaurantDetails(_ completion: (RestaurantDetails) -> Void) { 23 | 24 | completion(RestaurantDetails()) 25 | } 26 | 27 | func fetchMenuItem(_ completion: (MenuItemDetails) -> Void) { 28 | 29 | completion(MenuItemDetails()) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /solutions/devsprint-pedro-menezes-2/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved: -------------------------------------------------------------------------------- 1 | { 2 | "object": { 3 | "pins": [ 4 | { 5 | "package": "swift-snapshot-testing", 6 | "repositoryURL": "https://github.com/pointfreeco/swift-snapshot-testing.git", 7 | "state": { 8 | "branch": "main", 9 | "revision": "88f6e2c0afe04221fcfb1601a2ecaad83115a05f", 10 | "version": null 11 | } 12 | } 13 | ] 14 | }, 15 | "version": 1 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/AppDelegate/AppDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | @main 11 | class AppDelegate: UIResponder, UIApplicationDelegate { 12 | 13 | func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { 14 | 15 | DefaultsKey.user.value = User(userName: "John Appleseed", userEmail: "john@apple.com") 16 | DefaultsKey.adress.value = "Rua Bela Cintra, 495 - Consolação" 17 | DefaultsKey.paymentMethod.value = "Cartão de Crédito" 18 | 19 | return true 20 | } 21 | 22 | // MARK: UISceneSession Lifecycle 23 | 24 | func application(_ application: UIApplication, configurationForConnecting connectingSceneSession: UISceneSession, options: UIScene.ConnectionOptions) -> UISceneConfiguration { 25 | 26 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role) 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/AppDelegate/SceneDelegate.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SceneDelegate: UIResponder, UIWindowSceneDelegate { 11 | 12 | var window: UIWindow? 13 | private var coordinator: AppCoordinator? // 1 14 | 15 | func scene(_ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions) { 16 | 17 | guard let windowScene = (scene as? UIWindowScene) else { return } 18 | let navController = UINavigationController() 19 | 20 | coordinator = AppCoordinator(presenter: navController) 21 | 22 | coordinator?.start() 23 | 24 | window = UIWindow(windowScene: windowScene) 25 | window?.rootViewController = navController 26 | window?.makeKeyAndVisible() 27 | } 28 | } 29 | 30 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/AppCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 28/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | class AppCoordinator: Coordinator { 11 | var childCoordinators = [Coordinator]() 12 | var presenter: UINavigationController 13 | 14 | init(presenter: UINavigationController) { 15 | self.presenter = presenter 16 | } 17 | 18 | func start() { 19 | let child = HomeCoordinator(presenter: presenter) 20 | child.parentCoordinator = self 21 | child.start() 22 | } 23 | 24 | func childDidFinish(_ child: Coordinator?) { 25 | for (index, coordinator) in childCoordinators.enumerated() { 26 | if coordinator === child { 27 | childCoordinators.remove(at: index) 28 | break 29 | } 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/Coordinatable.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinatable.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | public protocol Coordinatable: AnyObject { 11 | func onFinish() 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/Coordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Coordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 28/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | protocol Coordinator: AnyObject { 11 | 12 | var childCoordinators: [Coordinator] { get set } 13 | var presenter: UINavigationController { get set } 14 | 15 | func start() 16 | } 17 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/HomeCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 28/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class HomeCoordinator: Coordinator { 11 | 12 | var childCoordinators = [Coordinator]() 13 | var presenter: UINavigationController 14 | weak var parentCoordinator: AppCoordinator? 15 | 16 | init(presenter: UINavigationController) { 17 | self.presenter = presenter 18 | } 19 | 20 | func start() { 21 | let viewController = HomeViewController() 22 | viewController.viewModel.coordinator = self 23 | presenter.pushViewController(viewController, animated: true) 24 | } 25 | 26 | func goToSettings() { 27 | let child = SettingsCoordinator(presenter: presenter) 28 | child.parentCoordinator = parentCoordinator 29 | childCoordinators.append(child) 30 | child.start() 31 | } 32 | 33 | func onFinish() { 34 | parentCoordinator?.childDidFinish(self) 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/Settings.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Settings.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol Settings: AnyObject { 11 | func goToSettings() 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Coordinators/SettingsCoordinator.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AppCoordinator.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 28/01/22. 6 | // 7 | 8 | import UIKit 9 | 10 | final class SettingsCoordinator: Coordinator { 11 | 12 | var childCoordinators = [Coordinator]() 13 | var presenter: UINavigationController 14 | weak var parentCoordinator: AppCoordinator? 15 | 16 | init(presenter: UINavigationController) { 17 | self.presenter = presenter 18 | } 19 | 20 | func start() { 21 | let viewController = SettingsViewController() 22 | viewController.viewModel.coordinator = self 23 | presenter.pushViewController(viewController, animated: true) 24 | } 25 | 26 | func onFinish() { 27 | parentCoordinator?.childDidFinish(self) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Extension/Data+Resource.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Data+DataSource.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Gustavo Soares on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Data { 11 | static func readData(from fileNamed: String) -> Data? { 12 | if let url = Bundle.main.url(forResource: fileNamed, withExtension: "json") { 13 | do { 14 | return try Data(contentsOf: url) 15 | } catch { 16 | return nil 17 | } 18 | } 19 | return nil 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/Address.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Address.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Lemos on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Address: Codable { 11 | 12 | let street: String 13 | let number: String 14 | let neighborhood: String 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/DeliveryTime.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryTime.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Gustavo Soares on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DeliveryTime: Codable { 11 | let minimum: Int 12 | let maximum: Int 13 | 14 | private enum CodingKeys: String, CodingKey { 15 | case minimum = "min" 16 | case maximum = "max" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/Restaurant.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Restaurant.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alley Pereira on 24/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Restaurant: Codable { 11 | let name: String 12 | let category: String 13 | let deliveryTime: DeliveryTime 14 | let reviews: Review 15 | let menu: [RestaurantItem] 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case name, category, reviews, menu 19 | case deliveryTime = "delivery_time" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/RestaurantItem.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantItem.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Gustavo Soares on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantItem: Codable { 11 | let category: String 12 | let name: String 13 | let price: Double 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/RestaurantsListModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantsListModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alley Pereira on 24/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RestaurantsListModel: Codable { 11 | let name: String 12 | let category: String 13 | let deliveryTime: DeliveryTime 14 | let reviews: Review 15 | let menu: [RestaurantItem] 16 | 17 | private enum CodingKeys: String, CodingKey { 18 | case name, category, reviews, menu 19 | case deliveryTime = "delivery_time" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Models/Review.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Review.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Gustavo Soares on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Review: Codable { 11 | let score: Double 12 | let count: Int 13 | } 14 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "pizza.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/pizza.imageset/pizza.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "restaurante-logo.jpeg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "idiom" : "universal", 14 | "scale" : "3x" 15 | } 16 | ], 17 | "info" : { 18 | "author" : "xcode", 19 | "version" : 1 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/devpass-tech/challenge-mvvm-delivery/d0aa46ce030286efe0b7126c6c06a7db3d277d2b/solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Assets.xcassets/restaurant-logo.imageset/restaurante-logo.jpeg -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Resources/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSAppTransportSecurity 6 | 7 | NSAllowsArbitraryLoads 8 | 9 | 10 | UIApplicationSceneManifest 11 | 12 | UIApplicationSupportsMultipleScenes 13 | 14 | UISceneConfigurations 15 | 16 | UIWindowSceneSessionRoleApplication 17 | 18 | 19 | UISceneConfigurationName 20 | Default Configuration 21 | UISceneDelegateClassName 22 | $(PRODUCT_MODULE_NAME).SceneDelegate 23 | 24 | 25 | 26 | 27 | 28 | 29 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/AddressSearch/AddressSearchViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AddressSearchViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class AddressSearchViewController: UIViewController { 11 | 12 | let searchController = UISearchController(searchResultsController: nil) 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | searchController.searchResultsUpdater = self 18 | searchController.searchBar.placeholder = "Rua, número, bairro" 19 | searchController.searchBar.delegate = self 20 | 21 | definesPresentationContext = true 22 | navigationItem.searchController = searchController 23 | navigationItem.title = "Address Search" 24 | navigationItem.hidesSearchBarWhenScrolling = false 25 | } 26 | 27 | override func viewDidLoad() { 28 | navigationController?.navigationBar.prefersLargeTitles = true 29 | } 30 | 31 | required init?(coder: NSCoder) { 32 | fatalError("init(coder:) has not been implemented") 33 | } 34 | 35 | override func loadView() { 36 | self.view = AddressListView() 37 | } 38 | } 39 | 40 | extension AddressSearchViewController: UISearchResultsUpdating { 41 | 42 | func updateSearchResults(for searchController: UISearchController) { 43 | 44 | } 45 | } 46 | 47 | extension AddressSearchViewController: UISearchBarDelegate, UISearchControllerDelegate { 48 | 49 | func searchBarTextDidEndEditing(_ searchBar: UISearchBar) { 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Home/HomeViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class HomeViewController: UIViewController { 11 | 12 | var viewModel: HomeViewModelType = HomeViewModel() 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | navigationItem.title = "Delivery App" 18 | navigationItem.rightBarButtonItem = UIBarButtonItem(title: "Settings", 19 | style: .plain, 20 | target: self, 21 | action: #selector(routeToSettings)) 22 | } 23 | 24 | required init?(coder: NSCoder) { 25 | fatalError("init(coder:) has not been implemented") 26 | } 27 | 28 | override func viewDidLoad() { 29 | navigationController?.navigationBar.prefersLargeTitles = true 30 | } 31 | 32 | override func loadView() { 33 | self.view = HomeView() 34 | } 35 | 36 | @objc func routeToSettings(_ sender: UITapGestureRecognizer) { 37 | viewModel.coordinator?.goToSettings() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Home/HomeViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class HomeViewModel: HomeViewModelType { 11 | 12 | var coordinator: HomeCoordinator? 13 | 14 | func onFinish() { 15 | coordinator?.onFinish() 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Home/HomeViewModelType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModelType.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol HomeViewModelType: Coordinatable { 11 | var coordinator: HomeCoordinator? { get set } 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/MenuItem/MenuItemViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuItemViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class MenuItemViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = MenuItemView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetails.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantDetailsViewController: UIViewController { 11 | 12 | override func loadView() { 13 | self.view = RestaurantDetailsView() 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/RestaurantDetails/RestaurantDetailsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetailsViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Murillo R. Araújo on 29/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RestaurantDetailsPresentable: AnyObject { 11 | func displayRestaurantDetails(with restaurant: Restaurant) 12 | func displayError(error: Error) 13 | } 14 | 15 | class RestaurantDetailsViewModel { 16 | 17 | private let service: DeliveryApiService 18 | 19 | weak var presenter: RestaurantDetailsPresentable? 20 | 21 | init(with service: DeliveryApiService = DeliveryServiceImplementation()) { 22 | self.service = service 23 | } 24 | 25 | func loadRestaurantDetails() { 26 | service.fetchRestaurantDetails { result in 27 | switch result { 28 | case .success(let restaurant): 29 | self.presenter?.displayRestaurantDetails(with: restaurant) 30 | 31 | case .failure(let error): 32 | self.presenter?.displayError(error: error) 33 | } 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/RestaurantList/RestaurantListViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantListViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 27/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class RestaurantListViewController: UIViewController { 11 | 12 | init() { 13 | super.init(nibName: nil, bundle: nil) 14 | 15 | navigationItem.title = "Restaurant List" 16 | } 17 | 18 | required init?(coder: NSCoder) { 19 | fatalError("init(coder:) has not been implemented") 20 | } 21 | 22 | override func loadView() { 23 | self.view = RestaurantListView() 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Settings/SettigsViewModelType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModelType.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol SettigsViewModelType: Coordinatable { 11 | var coordinator: SettingsCoordinator? { get set } 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Settings/SettingsViewController.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewController.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Rodrigo Borges on 31/10/21. 6 | // 7 | 8 | import UIKit 9 | 10 | class SettingsViewController: UIViewController { 11 | 12 | var viewModel: SettingsViewModel = SettingsViewModel() 13 | 14 | init() { 15 | super.init(nibName: nil, bundle: nil) 16 | 17 | navigationItem.title = "Settings" 18 | } 19 | 20 | required init?(coder: NSCoder) { 21 | fatalError("init(coder:) has not been implemented") 22 | } 23 | 24 | override func viewDidDisappear(_ animated: Bool) { 25 | super.viewDidDisappear(animated) 26 | viewModel.coordinator?.onFinish() 27 | } 28 | 29 | override func loadView() { 30 | self.view = SettingsView() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Settings/SettingsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewModel.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Strutzki on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | class SettingsViewModel: SettigsViewModelType { 11 | 12 | var coordinator: SettingsCoordinator? 13 | 14 | func onFinish() { 15 | coordinator?.onFinish() 16 | } 17 | 18 | func getName() -> String { 19 | DefaultsKey.user.value?.userName ?? "" 20 | } 21 | 22 | func getEmail() -> String { 23 | DefaultsKey.user.value?.userEmail ?? "" 24 | } 25 | 26 | func getAddress() -> String { 27 | DefaultsKey.adress.value ?? "" 28 | } 29 | 30 | func getPaymentMethod() -> String { 31 | DefaultsKey.paymentMethod.value ?? "" 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Screens/Settings/SettingsViewModelType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // HomeViewModelType.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol SettingsViewModelType: Coordinatable { 11 | var coordinator: Settings? { get set } 12 | } 13 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/Network/NetworkManager.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIService.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alley Pereira on 24/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NetworkManager { 11 | var session: URLSession { get } 12 | func get(request: URLRequest, completion: @escaping(Result) -> Void) 13 | } 14 | 15 | final class NetworkManagerService: NetworkManager { 16 | 17 | let session: URLSession 18 | private var dataTask: URLSessionTask? 19 | 20 | init(_ session: URLSession = .shared) { 21 | self.session = session 22 | } 23 | 24 | deinit { 25 | dataTask?.cancel() 26 | } 27 | 28 | func get(request: URLRequest, completion: @escaping (Result) -> Void) { 29 | dataTask = session.dataTask(with: request) { data, response, error in 30 | if let error = error { 31 | completion(.failure(.requestFailed(description: error.localizedDescription))) 32 | return 33 | } 34 | 35 | guard let data = data, !data.isEmpty else { 36 | completion(.failure(.emptyData)) 37 | return 38 | } 39 | 40 | completion(.success(data)) 41 | } 42 | dataTask?.resume() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/Network/Router.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Router.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alley Pereira on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Router { 11 | 12 | /// Create new cases for more requests 13 | case fetchRestaurants 14 | case fetchRestaurantDetails 15 | case fetchMenuItem 16 | case fetchAddress 17 | 18 | static private let baseURL: String = "https://raw.githubusercontent.com/devpass-tech/challenge-delivery-app/main/api" 19 | 20 | var url: URL { 21 | URL(string: Self.baseURL+self.path)! 22 | } 23 | 24 | private var path: String { 25 | switch self { 26 | case .fetchRestaurants: 27 | return "/home_restaurant_list.json" 28 | case .fetchRestaurantDetails: 29 | return "/restaurant_details.json" 30 | case .fetchMenuItem: 31 | return "/restaurant-details.json" 32 | case .fetchAddress: 33 | return "/address_search_results.json" 34 | } 35 | } 36 | 37 | var getRequest: URLRequest { 38 | var request = URLRequest(url: self.url) 39 | request.httpMethod = "GET" 40 | return request 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/Network/ServiceError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceError.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Alley Pereira on 24/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ServiceError: Error { 11 | case requestFailed(description: String) 12 | case emptyData 13 | case decodeError 14 | 15 | var localizedDescription: String { 16 | switch self { 17 | case .emptyData: 18 | return "No error was received but we also don't have data." 19 | case .requestFailed(description: let description): 20 | return "Could not run request because: \(description)" 21 | case .decodeError: 22 | return "Could not decoded result" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/Persistence/DefaultsKey.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DefaultsUser.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | public class DefaultsKey { 11 | static let user = DefaultsManager(key: "user") 12 | static let adress = DefaultsManager(key: "adress") 13 | static let paymentMethod = DefaultsManager(key: "payment-method") 14 | } 15 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/User.swift: -------------------------------------------------------------------------------- 1 | // 2 | // User.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct User: Codable { 11 | let userName: String 12 | let userEmail: String 13 | } 14 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallenge/Service/UserDefaults.swift: -------------------------------------------------------------------------------- 1 | // 2 | // UserDefaults.swift 3 | // DeliveryAppChallenge 4 | // 5 | // Created by Guilherme Prata Costa on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension UserDefaults { 11 | func removeAll() { 12 | for (key, _) in dictionaryRepresentation() { 13 | removeObject(forKey: key) 14 | } 15 | synchronize() 16 | } 17 | 18 | } 19 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/DeliveryAppChallengeTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryAppChallengeTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Rodrigo Borges on 25/10/21. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class DeliveryAppChallengeTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/Mocks/ApiServiceSpy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ApiServiceSpy.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Gustavo Soares on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | @testable import DeliveryAppChallenge 10 | 11 | final class APIServiceSpy: APIServiceProtocol { 12 | 13 | let session: URLSession 14 | 15 | init(session: URLSession) { 16 | self.session = session 17 | } 18 | 19 | var getCalled: Bool = false 20 | 21 | func get( 22 | request: URLRequest, 23 | of type: T.Type, 24 | completion: @escaping (Result) -> Void 25 | ) where T : Decodable { 26 | getCalled = true 27 | 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/Mocks/DeliveryApiServiceStub.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryServiceStub.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Gustavo Soares on 31/01/22. 6 | // 7 | 8 | import Foundation 9 | @testable import DeliveryAppChallenge 10 | 11 | final class DeliveryApiServiceStub: DeliveryApiService { 12 | var expectedRestaurants: Result<[Restaurant], ServiceError> = .success([]) 13 | var expectedItems: Result<[RestaurantItem], ServiceError> = .success([]) 14 | var expectedDetails: Result = .success(Restaurant(name: "", category: "", deliveryTime: DeliveryTime(minimum: 0, maximum: 0), reviews: Review(score: 10, count: 10), menu: [])) 15 | var expectedAddresses: Result<[Address], ServiceError> = .success([]) 16 | 17 | func fetchRestaurants(_ completion: @escaping (Result<[Restaurant], ServiceError>) -> Void) { 18 | completion(expectedRestaurants) 19 | } 20 | 21 | func fetchMenuItem(_ completion: @escaping (Result<[RestaurantItem], ServiceError>) -> Void) { 22 | completion(expectedItems) 23 | } 24 | 25 | func fetchRestaurantDetails(_ completion: @escaping (Result) -> Void) { 26 | completion(expectedDetails) 27 | } 28 | 29 | func searchAddresses(_ completion: @escaping (Result<[Address], ServiceError>) -> Void) { 30 | completion(expectedAddresses) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/Mocks/RestaurantDetailsPresentableMock.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetailsPresentableMock.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Murillo R. Araújo on 29/01/22. 6 | // 7 | 8 | import Foundation 9 | 10 | @testable import DeliveryAppChallenge 11 | 12 | class RestaurantDetailsPresentableMock: RestaurantDetailsPresentable { 13 | 14 | var displayedDetails = false 15 | var displayedErrors = false 16 | 17 | func displayRestaurantDetails(with restaurant: Restaurant) { 18 | displayedDetails = true 19 | } 20 | 21 | func displayError(error: Error) { 22 | displayedErrors = true 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/RestaurantDetailsViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RestaurantDetailsViewModelTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Murillo R. Araújo on 29/01/22. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | final class RestaurantDetailsViewModelTests: XCTestCase { 12 | 13 | private var sut: RestaurantDetailsViewModel! 14 | private var stub: DeliveryApiServiceStub! 15 | private var presenterSpy: RestaurantDetailsPresentableMock! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | stub = DeliveryApiServiceStub() 20 | presenterSpy = RestaurantDetailsPresentableMock() 21 | sut = RestaurantDetailsViewModel(with: stub) 22 | sut.presenter = presenterSpy 23 | } 24 | 25 | override func tearDown() { 26 | stub = nil 27 | sut = nil 28 | presenterSpy = nil 29 | super.tearDown() 30 | } 31 | 32 | func test_LoadRestaurantDetails_shouldDisplayDetails() throws { 33 | stub.expectedDetails = .success(makeRestaurantDetailMock()) 34 | sut.loadRestaurantDetails() 35 | XCTAssertTrue(presenterSpy.displayedDetails) 36 | } 37 | 38 | func testLoadRestaurantDetailsWithError() throws { 39 | stub.expectedDetails = .failure(.emptyData) 40 | sut.loadRestaurantDetails() 41 | XCTAssertTrue(presenterSpy.displayedErrors) 42 | } 43 | } 44 | extension RestaurantDetailsViewModelTests { 45 | private func makeRestaurantDetailMock() -> Restaurant { 46 | Restaurant(name: "Dummy", category: "Dummy", deliveryTime: DeliveryTime(minimum: 10, maximum: 10), reviews: Review(score: 10, count: 10), menu: []) 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/Service/DeliveryApiTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeliveryApiTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Gustavo Soares on 25/01/22. 6 | // 7 | 8 | import Foundation 9 | import XCTest 10 | 11 | @testable import DeliveryAppChallenge 12 | 13 | final class DeliveryApiTests: XCTestCase { 14 | // TODO: Create Spy for delivery api abstraction 15 | private var sut: DeliveryApi! 16 | 17 | override func setUp() { 18 | super.setUp() 19 | sut = DeliveryApi() 20 | } 21 | 22 | override func tearDown() { 23 | sut = nil 24 | super.tearDown() 25 | } 26 | 27 | func test_fetchMenuItems_shouldReturnValidMenuItems() { 28 | let expectation = expectation(description: "waiting api return") 29 | 30 | sut.fetchMenuItem { items in 31 | XCTAssertTrue(items.count > 0) 32 | expectation.fulfill() 33 | } 34 | wait(for: [expectation], timeout: 1.0) 35 | } 36 | 37 | func test_fetchMenuItems_shouldReturnNil() { 38 | XCTFail("Test not implemented yet =P") 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /solutions/devsprint-vinicius-carvalho-1/DeliveryAppChallengeTests/SettingsViewModelTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SettingsViewModelTests.swift 3 | // DeliveryAppChallengeTests 4 | // 5 | // Created by Guilherme Strutzki on 29/01/22. 6 | // 7 | 8 | import XCTest 9 | @testable import DeliveryAppChallenge 10 | 11 | class SettingsViewModelTests: XCTestCase { 12 | 13 | func testSettingsViewModel() { 14 | let settingsViewModel = SettingsViewModel() 15 | let name = "John Appleseed" 16 | let address = "Rua Bela Cintra, 495 - Consolação" 17 | let email = "john@apple.com" 18 | let paymentMethod = "Cartão de Crédito" 19 | 20 | XCTAssertEqual(settingsViewModel.getName(), name) 21 | XCTAssertEqual(settingsViewModel.getEmail(), email) 22 | XCTAssertEqual(settingsViewModel.getAddress(), address) 23 | XCTAssertEqual(settingsViewModel.getPaymentMethod(), paymentMethod) 24 | } 25 | } 26 | --------------------------------------------------------------------------------