The-Book-of-VIPER icon indicating copy to clipboard operation
The-Book-of-VIPER copied to clipboard

Вопросы по подходу Rambler к VIPER

Open delebedev opened this issue 9 years ago • 45 comments

Ребята, вы очень больше дело делаете, спасибо! У меня есть несколько вопросов, некоторые из которых я думаю будет полезно осветить в том или ином виде в книге про VIPER (если просто ответите в тикете тоже буду благодарен)

  • Средний размер ваших Rambler приложений (я не думаю что это уж совсем секретная инфа). Из разряда "количество модулей" + LOC. Это поможет читателю понять когда VIPER является действительно оправданным (и отнести его проект к маленькому / немаленькому)
  • Производительность Typhoon (оно не совсем к делу относится, но я вижу вы серьезно относитесь к инструменту). Я видел проекты с ~10 средними assembly которые на устройствах уровня iPhone 4 жутко тормозят, на старте, именно из-за Typhoon. Тут как раз поможет пункт 1 чтобы понять масштабы ваших assembly
  • VIPER + Reactive, VIPER + Promises и так далее. Смотрели ли в эту сторону (думаю что да :smile: ) и возможно какие-то рекомендации.

delebedev avatar Nov 25 '15 18:11 delebedev

Привет, VIPER +Reactive, VIPER + Promises. Безусловно, что мысли о promises/reactive возникают за счет простой и явной структуры потока событий/данных в VIPER. Но на данный момент для нас совсем не понятно, зачем они нужны в VIPER. Использование синхронных методов там, где это возможно, позволяет и повысить читаемость, и упростить тестирование. Возможно, что на одном из проектов мы опробуем Promises/Reactive в качестве пилота. Тогда расскажем на Rambler.iOS обязательно.

mogol avatar Nov 28 '15 06:11 mogol

@garnett Привет! Спасибо за отзыв и помощь в развитии :)

Касательно производительности Typhoon. У любого стороннего компонента всегда есть как свои плюсы, так и минусы. В случае Typhoon небольшие задержки на старте являются ценой за его использование. На самом деле, все не так критично, и в этом направлении постоянно ведется работа - @alexgarbarev, думаю, сможет вбросить информации на эту тему.

Вообще есть быстрый способ уменьшить это время. Если в проекте не используется автоинъекция, добавь в Info.plist ключ TyphoonAutoInjectionEnabled со значением в NO - все сразу станет намного лучше :)

Па второй части вопроса - Assembly в некоторых проектах бывает очень много. В моем текущем, к примеру, их порядка 40-50 штук, и количество еще будет расти.

etolstoy avatar Nov 28 '15 15:11 etolstoy

@garnett Да, по поводу производительности Typhoon, интересно было бы посмотреть отчеты из профайлера, где, собственно, оно тормозит. Да, AutoInjection действительно замедляет производительность - оно сканирует все классы компонентом и ищет auto-injection свойства на старте приложения. Отключение этой функции может существенно сократить время старта. В остальном, мне кажется, проблемы с производительностью при старте быть не должно (А если есть - нужно посмотреть в чем и пофиксить)

alexgarbarev avatar Nov 28 '15 15:11 alexgarbarev

@garnett Средний размер ваших Rambler приложений... LOC: 30k-60k Модулей: 10-40

mogol avatar Dec 08 '15 09:12 mogol

Добрый день, Очень понравился подход Rambler к VIPER. Буду крайне признателен, если вы поясните как у вас реализуется роутинг в приложениях на Swift. Насколько я понял, он будет отличаться от роутинга в приложениях на ObjC. В частности, интересно каким образом вы реализуете доступ роутера текущего модуля к ModuleInput следующего.

andmedved avatar Jan 29 '16 06:01 andmedved

Добрый день!

В Swift-версии, чтобы не использовать свизлинг, был создан базовый контроллер, который содержит в себе реализацию метода prepareForSegue. В ней у контроллера проверяется его соответствие специальному протоколу ViperModuleInputProvider. Этот протокол обязует класс иметь свойство moduleInput. Если протокол реализован, то moduleInput передается в блок конфигурации(который передается через segue.sender).

AndreyZarembo avatar Jan 29 '16 15:01 AndreyZarembo

Здравствуйте.

Хотелось понять как в свифте без свизлинга происходит процесс перехода в другой модуль? Например, пользователь нажал на ячейку таблицы - надо нарисовать детальный контроллер по какому-то ID из ячейки. Вью-контроллер начал раскручивать сегвей, в какой момент управление попадает в роутер, если свизлинга нет? Получается или в сториборде нет сегвеев, или сторибордовые сегвеи надо душить в делегате перед рождением и отличать их от своих, чтобы не того не задушить.

pomozoff avatar Feb 15 '16 14:02 pomozoff

Воспользуюсь темой чтобы задать вопрос. Мастера, какое может быть решение для определения нужно показывать модуль или нет? Например, хотим показать обучалку один раз, но, перед тем как это делать, нужно проверить нету ли флага, что мы уже его показывали, есть ли файл с обучалкой и т.д. Обо всем этом по идее знает только интерактор, но на этом этапе до него далеко. Буду очень рад помощи.

letko-dmitry avatar Apr 14 '16 09:04 letko-dmitry

@letko-dmitry Привет. Отличный вопрос. Верно, эта логика находится в интеракторе, но не самого модуля "обучалки", модуля из которого он будет показываться. То есть получается следующий workflow:

  1. По определенному событию, например событию о готовности view(viewDidAppear), presenter узнает, что пора показать "обучалку".
  2. Presenter запрашивает у interactor'a, нужно ли ее показывать.
  3. В результате ответа, interactor сообщает роутеру, что нужно открыть модуль.
  4. Показывается модуль "обучалки" Такой подход мы используем во многих местах - например показ UIPopoverController. Для того что бы код не дублировался и легко переиспользовался, логику принятия решения можно вынести в хелпер interactor'a (solver), а логику показа в хелпер роутера(revealer).

CognitiveDisson avatar Apr 14 '16 12:04 CognitiveDisson

Всем привет. Хотелось узнать, как вы определяете какой экран нужно отображать при запуске приложения. Например, как пропускать экран авторизации для залогиненного пользователя?

ct4h avatar Apr 21 '16 11:04 ct4h

@сt4h Привет! Удобно в AppDelegate создвать стек экранов и отображать его. Вдвойне удобно, если распилить делегать по функциям с помощью https://github.com/rambler-ios/RamblerAppDelegateProxy.

Это позволяет правильно отобразить экраны при запуске для нового/старого/авторизованного пользователя, обработать диплинки и пуши.

AndreyZarembo avatar Apr 21 '16 11:04 AndreyZarembo

@AndreyZarembo спасибо за оперативный ответ.

ct4h avatar Apr 21 '16 12:04 ct4h

Ребята привет. Интересуюсь VIPER. У меня есть вопрос о том как осуществлять переход на следующий модуль. Во первых Presenter держит weak ссылку на View. Получается больше никто не держит strong ссылку на View (до тех пор пока она не добавится в стек экранов). Но у вас сказано что переход на следующий модуль выполняет Router. Знает ли Router о View? Как он получает вью. Как создавать View с использованием Typhoon, ведь ее никто не держит (Presenter-weak property->View) и она убивается. Приходится выставлять scope = TyphoonScopeSingleton. Но если у нас много экранов - проблема памяти? Можете ли поподробнее об этом рассказать.

Во вторых. Куда обращается Presenter текущего модуля если нужно перейти на другой экран? Правильно ли я понял что он держит ссылку на Router следующего модуля и вызывает его метод? Так ли что весь Assembly слой модуля скрыт в Typhoon и в модуле нету ссылок на Assembly.

Спасибо!

iharkatkavets avatar Jun 17 '16 06:06 iharkatkavets

@igorkotkovets Привет. Да, все верно, весь модуль держится в памяти за счет view. На view только две слабых ссылки - одна у Presenter и одна у Router текущего модуля. Переход на следующий экран осуществляет Router текущего модуля (view закрыта протоколом для перехода <RamblerViperModuleTransitionHandlerProtocol>), когда об этом просит Presenter. При этом Router создает viewController следующего модуля и сразу добавляет в стек, но не сохраняет на него ссылку. Стек навигации держит контроллер и весь модуль. Когда создается контроллер, Typhoon создаст и остальный части модуля. В основном мы используем следующие способы создания контроллера:

  • создавать вручную
 [TyphoonDefinition withFactory:[self.storyboardAssembly someStoryboard]
                                  selector:@selector(instantiateViewControllerWithIdentifier:)
  • использовать uistoryboardsegue

Для удобства мы используем ViperMcFlurry.

Вся логика построения модуля содержится в Assembly и ссылки на нее у частей модуля нет. Но если мы создаем контроллер вручную, то мы закрываем Assembly протоколом <SomeModuleFactory> в котором есть метод для создания. И инжекстируем эту фабрику в Router модуля, который хочет открыть экран.

Presenter не держит ссылку на Router другого модуля, только на свой.

Более подробно можно посмотреть в RamblerConferences.

CognitiveDisson avatar Jun 17 '16 07:06 CognitiveDisson

Здравствуйте! Большое вам спасибо за то что вы делаете!

У меня есть вопрос по поводу передачи данных в другой модуль на Swift. Поле moduleInput у протокола ModuleInputProvider это класс реализующий протокол ModuleInput? И не совсем понимаю что такое блок конфигурации. Заранее спасибо!

phil-alekhin avatar Aug 01 '16 08:08 phil-alekhin

Добрый день. Возник такой вопрос: обратил внимание что в вашем приложении https://github.com/rambler-digital-solutions/rambler-it-ios ссылка на другие сториборды часто (может и всегда, все сториборды не проверял) идет через идентификатор контроллера, а не через установку у одного из контроллеров своства  is initial view controller, даже если других точек входа в сториборду нет (например, в Report.storyboard только два вьюконтроллера и только у одного из них есть идентификатор). Хотелось бы уточнить - дело только в личных предпочтениях или же есть какая-то иная причина, которую я не понял?

aknew avatar Oct 16 '16 15:10 aknew

@aknew Как я понял, так просто удобнее. В твоем примере ссылка на этот контроллер идет из Main.storyboard. Гораздо легче из роутера просто дернуть segue и uikit сделает все за тебя (инициализирует контроллер из нужного сториборда), чем самому указывать сториборд и вызывать у наго instantiateInitialViewController. Т.е. из кода нам даже не нужно знать, что нужный нам контроллер находиться в другом сториборде, мы просто дергаем segue. P.S. я не из Rambler'a. Возможно, у ребят был какой-то другой мотив.

maxsava avatar Oct 16 '16 16:10 maxsava

Ты не понял, вопрос не про seque как подход - с ним все понятно. Вопрос в том что ссылку на контроллер в другой сториборде можно установить двумя способами:

  1. указать в сториборде один из контроллеров как точку входа и тогда он будет цепляться автоматом при создании ссылки на эту сториборду в другой
  2. указать у контроллера в ториборде идентификатор и прописать его в ссылке на сториборду вручную (т.к. автоподстановки нет), а это потенциальный источник ошибок т.к. вроде бы на этапе сборки оно не проверяется Ребята из Рамблера почему-то используют второй способ, хотя и с первым никаких проблем вроде как не возникает (я пробовал), при этом на первый взгляд в большинстве случаев иметь несколько точек входа в сториборду им не надо. Вот я и интересуюсь - не упустил ли я чего, может, есть какая-то причина или подход который я не понял?

aknew avatar Oct 16 '16 16:10 aknew

@aknew Спасибо за вопрос! Как раз таки в основном из-за того, что может быть несколько точек входа в сториборду и лучше в явном виде указывать, на какой экран мы хотим перейти.

etolstoy avatar Oct 16 '16 19:10 etolstoy

Спасибо за ответ

aknew avatar Oct 17 '16 19:10 aknew

Здравствуйте,

Меня интересует вопрос по архитектуре. Приведу пример, допустим у меня в приложении есть какой-то сервис закачек файлов, который может работать в фоне. Есть какой-то модуль, который использует данные и отображает количество загруженых файлов и тп. Так вот, какой компонент модуля (презентер или интерактор) держит ссылку на сервис и получает данные от него? Мне кажется, что если добавить ссылку на сервис в интерактор - то это добавляет избыточности, порождает дополнительные пробросы Service->Interactor->Presenter. Но если я правильно понимаю, презентер не должен держать ссылку на сервис.

Как вы поступаете в этом случае?

iharkatkavets avatar Dec 28 '16 20:12 iharkatkavets

@igorkotkovets Приветствую! Стандартное решение - интерактор, именно он ответственен за взаимодействие с сервисным слоем, соответственно он и держит ссылку на сервис.

В случае, если разрешить подключение сервиса в Presenter в данном случае, то может возникнуть желание делать это и в дальнейшем. Происходит смешение ответственностей P и I, чего допускать не стоит.

DevAlloy avatar Dec 28 '16 20:12 DevAlloy

@DevAlloy спасибо за ответ!

iharkatkavets avatar Dec 28 '16 20:12 iharkatkavets

Добрый день. Есть несколько вопроса:

  1. Переход между модулями в swift - как он осуществляется? Я так понял, используются extension для UIViewController, но при этом шаблон swifty_viper не добавляет во вью такой протокол, а роутер и вовсе вещь в себе без связи с чем-либо кроме протокола RouterInput т.е. перейти из него куда-либо не получится. Что это, недоработка шаблона или я что-то не догоняю (что легко может быть особенно в свифте)?
  2. Составные модули - как осуществляется роутинг в них, через moduleOutput? Допустим, у меня есть задача реализовать ячейку информации о докладе с переходом по клику на полный текст доклада и переходом по клику на какую-то кнопку на ячейке в редактирование информации о докладе, при этом ячейка будет видна из списка докладов конференции и из списка докладов в профиле автора
  3. Немного не по теме - вы советуете использовать Typhoon, как вы решаете проблему с анализом проекта (пункт меню product->analyze он же clang-analyze)? По моим наблюдениям, т.к. Typhoon устанавливает связи в рантайме анализатор оказывается неспособным их проследить, вырожденный пример с делением на нуль могу накидать. С уважением

aknew avatar Jan 24 '17 22:01 aknew

@aknew 1. https://github.com/strongself/The-Book-of-VIPER/issues/21

iSevenDays avatar Jan 30 '17 06:01 iSevenDays

Спасибо, но это не совсем то - по ссылке описано как использовать objc-протокол из ViperMcFlurry (я кстати так уже сделал к моменту написания вопроса), но вот тут https://www.youtube.com/watch?v=m4MYKzlqtH8 утверждалось вроде что можно сделать через extantions. Впрочем, вопрос еще и в том что ни один из этих способов, по-видимому, не заложен в темплейт swifty_viper

aknew avatar Feb 13 '17 07:02 aknew

@aknew В видео говорилось про расширения протоколов. Тут есть реализация переходов между модулями с помощью transition handler-а, но пока без передачи данных.

svvoff avatar Feb 23 '17 16:02 svvoff

День добрый. Как правильно реализовать левое меню через VIPER? В голову приходят варианты:

  1. SideMenuModule (SMM), MenuModule (MM), ContentModule (CM) SMM отвечает за саму логику выдвижной панели, получает необходимый экран от Module Output у MM и в Router'e меняет CM
  2. SideMenuModule (SMM), ContentModule (CM) Здесь SMM выполняет роль выдвижной панели + само меню, соответственно Presenter получает уведомления от View, и передает их Router'у, который меняет CM
  3. Что-то лучше?

ManWithBear avatar Mar 09 '17 13:03 ManWithBear

Первый вариант самый предпочтительный на мой вгляд

DevAlloy avatar Mar 09 '17 13:03 DevAlloy

@DevAlloy благодарю.

ManWithBear avatar Mar 09 '17 13:03 ManWithBear