GithubHelp home page GithubHelp logo

recipes's Introduction

Recipes

Swift iOS application based on MVVM(non-reactive) pattern.

Screenshots:

SPM:

  • Kingfisher
  • SnapKit

Требования:

Главный экран:

  • Спискок рецептов. Каждый элемент списка содержит:
    • фотографию рецепта;
    • название рецепта;
    • краткое описание.
  • Возможность сортировки либо по названию рецепта, либо по дате последнего обновления.
  • Возможность поиска по любому из трех полей: названию, описанию, инструкциям. Поле поиска должно отображаться над всем контентом.

Детали рецепта:

  • По нажатию на ячейку должен открываться экран деталей рецепта, который содержит:
    • фотографии рецепта;
    • просмотр изображения рецепта на отдельном экране и его сохранение на телефон;
    • название рецепта;
    • краткое описание;
    • инструкции по приготовлению;
    • уровень сложности: от 1 до 5;
    • список похожих рецептов - переход к рецепту по нажатию.

recipes's People

Contributors

clmct avatar

Watchers

 avatar

recipes's Issues

Замечания по тестовому заданию #1

Замечания по UI:

  • Если зайти в детали заказа без интернета и выйти (свайпом), то индикатор загрузки будет поверх списка блюд
  • Криво работает переход из портретного режима в альбомный (текста заезжают за safeArea, галерея изображений ломается и т.д.)
  • Нет иконки приложения (минор)
  • Нет экрана загрузки (минор)

Замечания по коду (общие):

  • associatedtype model - наименование типов всегда пишется с большой буквы
  • Lazy св-ва лучше делать по необходимости. Настройку каждого subview лучше вынести в отдельные методы, для удобочитаемости и что бы св-ва не занимали столько места.
  • Пройтись по всем функциям и проставить модификаторы доступа, где это необходимо
  • Для удобочитаемости можно разбивать класс используя MARK на св-ва/жц/приватные методы/публичные методы
  • Есть утечки памяти, нужно проверить все замыкания и связи на retain cycle
  • Модели должны храниться каждая в своем файле (наименование класса = название файла). Исключением может быть protocol + базовый класс и то я бы советовал тоже разделять в разные файлы.

RecipesViewController:

  • func didUpdateViewModel функции должны указывать действие без вспомогательных глаголов (действие еще не совершено) updateViewModel
  • let h = hud имена функций и переменных всегда должны быть осмысленными, даже в рамках небольшого контекста (h не приемлемо)
  • navigationController?.navigationBar.prefersLargeTitles = true - navigationController не должен настраиваться из UIViewController, это противоречит принципам solid
  • viewModel?.filteredRecipes.count ?? 0 и 112 + 26 если функция что-то возвращает, лучше явно указывать ключевым словом return (исключение только inline замыкания)
  • private extension RecipesViewController с публичными методами может запутать того, кто будет проверять код. В рамках одного метода не понятно является он публичным или нет, плюс расширением эти функции не являются, это базовые функции класса и выносить их в extension нет необходимости (нарушается сама идея этого ключевого слова)
  • HudView(inView: self.navigationController?.view ?? self.view, type: type) если нужно отобразить hud на весь экран, лучше делегировать эту ответственность router’у, который в явном виде знает о NavigationController
    RecipesViewModel:
  • didHud из названия не совсем ясно что нужно сделать (didRequestShowHUD)?
  • func didSelect - замечание аналогичное func didUpdateViewModel
  • RecipesViewModelProtocol - св-ва и функции перемешаны. Сначала должны быть описаны все св-ва, затем все функции
  • let router: RouterProtocol - тут скорее всего retain cycle. Не уверен, что это хорошая идея напрямую инжектить роутер во viewModel, т.к. для viewModel открыты методы, о которых ей знать не надо. Лучше воспользоваться партерном Delegate
  • var isFiltering: Bool = false - нигде не используется
  • updateSearchResults(searchController: UISearchController) - viewModel не должна знать ничего о View. Передавать нужно не UI компоненты, а данные простого типа или модели (в данном случае, это text и index)
  • filter({…}) если замыкание является последним параметром и оно в единственном числе (больше замыканий нету), то можно опустить скобки и явное наименование параметра (trailing closure)
  • case 0: - лучше осмыслить числа именованными константами (или кастомным енамом), это сделает код более читаемым (case Constants.nameFilterIndex)
    RecipeTableViewCell:
  • identifire -> identifier
    DetailRecipeViewController:
  • Цвета и шрифты надо вынести в отдельный класс констант или расширение к UIColor/UIFont (это касается всего приложения). Если шрифт нативный, нужно завернуть его в кастомное название, что бы при смене шрифта достаточно было изменить его в одном месте
  • // let tap - закомментированного кода не должно быть (если не нужен лучше удалить, всегда можно восстановить по истории в гите)
  • Аналогичные замечания про расширения, наименование функций и переменных
  • DispatchQueue.main.async думаю, лучше заворачивать обработку результата внутри ViewModel, что бы не делать это для каждого замыкания снаружи
  • DetailRecipeViewController имеет публичный extension с настройкой элементов, который выглядит довольно громоздко. Тут надо избавиться от него двумя шагами - 1. Перенести функции в класс и сделать их приватными. 2. Уменьшить кол-во subview путем разделения UI на несколько блоков (отдельными View-контейнерами)
    PhotoViewModel:
  • Работу с PHPhotoLibrary стоит вынести и завернуть в отдельный класс
    HudView:
  • Судя по всему это не только HUD, но еще и экран отображения ошибок. Стоит разделить эти экраны и отображать индикатор загрузки непосредственно на экране, а ошибку уже исходя из дизайна (если это фуллскрин)
    NetworkService:
  • Можно закрыть сервис протоколами с методами для работы с конкретными моделями (например getRecipes() -> Recipes) и вызывать уже их.
    Inte+getDate:
  • Лучше не делать такие расширения, т.к. не из всех чисел можно получить дату и не везде этот функционал нужен. Использовать форматирование лучше по месту
    String+replace:
  • Не понятен смысл этого расширения. Внутри используется тоже самое, что можно сделать снаружи. Плюс не очень хорошо делать дефолтными значениями значения под конкретные случаи (“
    ” и “\n”)
  • NSString.CompareOptions.literal -> можно просто .literal

На остальных экранах внести исправления опираясь на вышеописанные

Замечания по тестовому заданию #2

Замечания по UI:

  • Экран ошибки теперь отличен от дизайна
  • Нет иконки приложения (минор)
  • Нет экрана загрузки (минор)

Замечания по коду (общие):

  • Настройку каждого subview лучше вынести в отдельные методы, для удобочитаемости и что бы св-ва не занимали столько места (не исправлено)

private lazy var photoScrollView: UIScrollView = {
let scroll = UIScrollView()
scroll.isPagingEnabled = true
scroll.showsHorizontalScrollIndicator = false
scroll.delegate = self
return scroll
}()

такой подход имеет ряд недостатков, лучше делать это в отдельном методе:

private let scrollView = UIScrollView()
...
private func setupScrollView() {
scrollView.isPagingEnabled = true
scrollView.showsHorizontalScrollIndicator = false
scrollView.delegate = self
}

  • Есть проблемы с наименованием функций, надо стараться подбирать такое название, что бы оно четко отображало то, что происходит в теле функции

RecipesViewController:

  • func bindUpdateViewModel звучит странно, лучше назвать bindToViewModel
  • updateTableView, showHUD, showLoader названия функций не отражают их реальное назначение, на деле в них биндинг, который, по идее, можно весь оставить в корневой функции bindToViewModel
  • 112 + 26 зачем эта сумма? Если эти значения что-то обозначаю, то нужно вынести их в константы и дать текстовое обозначение. Иначе лучше просто делать return 138
  • HudView(inView: self.view, type: type) все таки не очень хорошо, что показом HUDа занимается он сам, эта ответственность должна быть на том, кто его показывает (откуда его надо показать):

let hud - HudView(networkType: error)
self.addSubview(hud)
...

Вообще можно вынести показ HUDа отдельным протоколом и базовой реализацией, а для конкретных экранов уже подписываться этим протоколом и вызывать базовую реализацию (если не понятно скину пример)

RecipesViewModel:

  • let router: RouterProtocol - тут скорее всего retain cycle. Не уверен, что это хорошая идея напрямую инжектить роутер во viewModel, т.к. для viewModel открыты методы, о которых ей знать не надо. Лучше воспользоваться партерном Delegate (этот пункт стоит посмотреть)

RecipeTableViewCell:

  • тут тоже стоит переделать хранение и настройку свойств (subviews которые)

HudView:

  • Тут все таки надо подумать о разделении показа индикатора от ошибки или переименовать View, что бы было понятно что оно отображает

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.