├── .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 |
10 |
11 |
12 |
13 |
14 |
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