Первым делом нужно скачать все зависимости. Большинство из них установлены через SwiftPackageManager, но API Яндекс карт еще не до конца его поддерживает, поэтому его придется устанавливать через CocoaPods. Для этого зайдя в директорию с проектом нужно ввести:
pod install
Далее, зайдя в созданный workspace, нужно добавить переменную окружения в активную схему. Переменную назвать YandexAPIKey
, значение - ваш API ключ для работы с картами Яндекса.
Перед вами мое решение тестового задания компании SevenWinds на позицию iOS разработчика. Требования:
- Реализация на VIPER
- Реализовать 6 экранов/модулей:
- Логин для зарегистрированных пользователей
- Регистрация для нехарегистрированных пользователей
- Список кофеен
- Отображение кофеен на карте. Использовать YandexMap
- Список доступных товаров в кофейне
- Экран с подтверждением заказа
- Для авторизации используется JWT токен
- В компании используется Alamofire и Moya, поэтому я решил использовать одного из них для APIClient. Выбрал Alamofire, так как больше с ним знаком
Полный список требований доступен по этой ссылке
Swagger с методами API доступен по данной ссылке
Figma с макетами экранов доступна по третьей ссылке
Проект разбит на тестовые случаи/модули, как того требует VIPER. Каждый модуль лежит в папке, названной по тестовому случаю: Login, Register, CoffeeshopList, MenuDetails, CoffeeshopListMapView, BuyItemsModule. Все протоколы, требуемые для каждого модуля, содержатся в файле, название которого оканчивается на Contracts.
Так как проект был сделан не для коммерческого использования, и у меня нет оплаченного аккаунта Apple разработчика, я не имел доступ к Keychain, в котором стоило бы хранить введенные логин и пароль пользователя и JWT token. Вместо этого для простоты эти данные сохранялись в UserDefaults.
Все зависимости реализуют тот или иной протокол, так что переход на Keychain, как и тестирование кода, будет безболезненным.
Отдельно хочу обратить внимание на APIClient
.
Нужда в обновлении токена может появиться в любом модуле, и я решил, что таскать эту зависимость по всем модулям неразумно. Поэтому я передаю APIClient
'у AppStateService, что позволяет APIClient
'у, используя Credentials, незаметно для пользователя обновить JWT token, в случае, если тот устарел. Для этого я добавил метод withTryAuth(completion: @escaping () -> Void)
и вызываю его в случае, если сервер вернул ошибку 401. Токен в данной реализации сервера истекает раньше, чем заявлено, поэтому каждый ответ от сервера я проверяю на наличие ошибки 401
Бизнес логику приложения и хранение данных модуля я положил в Presenter
, так как это компонент, который связывает все остальные компоненты, и было бы неэффективно обращаться к Interactor
'у каждый раз, когда нам понадобится те или иные данные для отображения на экране или для передачи в Router
. Interactor
в моем представлении выполняет роль исключительно API воркера и, если бы была нужда, - воркера с хранилищем устройства.