Open delebedev opened 8 years ago
Привет, VIPER +Reactive, VIPER + Promises. Безусловно, что мысли о promises/reactive возникают за счет простой и явной структуры потока событий/данных в VIPER. Но на данный момент для нас совсем не понятно, зачем они нужны в VIPER. Использование синхронных методов там, где это возможно, позволяет и повысить читаемость, и упростить тестирование. Возможно, что на одном из проектов мы опробуем Promises/Reactive в качестве пилота. Тогда расскажем на Rambler.iOS обязательно.
@garnett Привет! Спасибо за отзыв и помощь в развитии :)
Касательно производительности Typhoon. У любого стороннего компонента всегда есть как свои плюсы, так и минусы. В случае Typhoon небольшие задержки на старте являются ценой за его использование. На самом деле, все не так критично, и в этом направлении постоянно ведется работа - @alexgarbarev, думаю, сможет вбросить информации на эту тему.
Вообще есть быстрый способ уменьшить это время. Если в проекте не используется автоинъекция, добавь в Info.plist
ключ TyphoonAutoInjectionEnabled
со значением в NO
- все сразу станет намного лучше :)
Па второй части вопроса - Assembly в некоторых проектах бывает очень много. В моем текущем, к примеру, их порядка 40-50 штук, и количество еще будет расти.
@garnett Да, по поводу производительности Typhoon, интересно было бы посмотреть отчеты из профайлера, где, собственно, оно тормозит. Да, AutoInjection действительно замедляет производительность - оно сканирует все классы компонентом и ищет auto-injection свойства на старте приложения. Отключение этой функции может существенно сократить время старта. В остальном, мне кажется, проблемы с производительностью при старте быть не должно (А если есть - нужно посмотреть в чем и пофиксить)
@garnett Средний размер ваших Rambler приложений... LOC: 30k-60k Модулей: 10-40
Добрый день, Очень понравился подход Rambler к VIPER. Буду крайне признателен, если вы поясните как у вас реализуется роутинг в приложениях на Swift. Насколько я понял, он будет отличаться от роутинга в приложениях на ObjC. В частности, интересно каким образом вы реализуете доступ роутера текущего модуля к ModuleInput следующего.
Добрый день!
В Swift-версии, чтобы не использовать свизлинг, был создан базовый контроллер, который содержит в себе реализацию метода prepareForSegue. В ней у контроллера проверяется его соответствие специальному протоколу ViperModuleInputProvider. Этот протокол обязует класс иметь свойство moduleInput. Если протокол реализован, то moduleInput передается в блок конфигурации(который передается через segue.sender).
Здравствуйте.
Хотелось понять как в свифте без свизлинга происходит процесс перехода в другой модуль? Например, пользователь нажал на ячейку таблицы - надо нарисовать детальный контроллер по какому-то ID из ячейки. Вью-контроллер начал раскручивать сегвей, в какой момент управление попадает в роутер, если свизлинга нет? Получается или в сториборде нет сегвеев, или сторибордовые сегвеи надо душить в делегате перед рождением и отличать их от своих, чтобы не того не задушить.
Воспользуюсь темой чтобы задать вопрос. Мастера, какое может быть решение для определения нужно показывать модуль или нет? Например, хотим показать обучалку один раз, но, перед тем как это делать, нужно проверить нету ли флага, что мы уже его показывали, есть ли файл с обучалкой и т.д. Обо всем этом по идее знает только интерактор, но на этом этапе до него далеко. Буду очень рад помощи.
@letko-dmitry Привет. Отличный вопрос. Верно, эта логика находится в интеракторе, но не самого модуля "обучалки", модуля из которого он будет показываться. То есть получается следующий workflow: 1) По определенному событию, например событию о готовности view(viewDidAppear), presenter узнает, что пора показать "обучалку". 2) Presenter запрашивает у interactor'a, нужно ли ее показывать. 3) В результате ответа, interactor сообщает роутеру, что нужно открыть модуль. 4) Показывается модуль "обучалки" Такой подход мы используем во многих местах - например показ UIPopoverController. Для того что бы код не дублировался и легко переиспользовался, логику принятия решения можно вынести в хелпер interactor'a (solver), а логику показа в хелпер роутера(revealer).
Всем привет. Хотелось узнать, как вы определяете какой экран нужно отображать при запуске приложения. Например, как пропускать экран авторизации для залогиненного пользователя?
@сt4h Привет! Удобно в AppDelegate создвать стек экранов и отображать его. Вдвойне удобно, если распилить делегать по функциям с помощью https://github.com/rambler-ios/RamblerAppDelegateProxy.
Это позволяет правильно отобразить экраны при запуске для нового/старого/авторизованного пользователя, обработать диплинки и пуши.
@AndreyZarembo спасибо за оперативный ответ.
Ребята привет. Интересуюсь VIPER. У меня есть вопрос о том как осуществлять переход на следующий модуль. Во первых Presenter держит weak ссылку на View. Получается больше никто не держит strong ссылку на View (до тех пор пока она не добавится в стек экранов). Но у вас сказано что переход на следующий модуль выполняет Router. Знает ли Router о View? Как он получает вью. Как создавать View с использованием Typhoon, ведь ее никто не держит (Presenter-weak property->View) и она убивается. Приходится выставлять scope = TyphoonScopeSingleton. Но если у нас много экранов - проблема памяти? Можете ли поподробнее об этом рассказать.
Во вторых. Куда обращается Presenter текущего модуля если нужно перейти на другой экран? Правильно ли я понял что он держит ссылку на Router следующего модуля и вызывает его метод? Так ли что весь Assembly слой модуля скрыт в Typhoon и в модуле нету ссылок на Assembly.
Спасибо!
@igorkotkovets Привет. Да, все верно, весь модуль держится в памяти за счет view. На view только две слабых ссылки - одна у Presenter и одна у Router текущего модуля. Переход на следующий экран осуществляет Router текущего модуля (view закрыта протоколом для перехода
[TyphoonDefinition withFactory:[self.storyboardAssembly someStoryboard]
selector:@selector(instantiateViewControllerWithIdentifier:)
Для удобства мы используем ViperMcFlurry.
Вся логика построения модуля содержится в Assembly и ссылки на нее у частей модуля нет. Но если мы создаем контроллер вручную, то мы закрываем Assembly протоколом
Presenter не держит ссылку на Router другого модуля, только на свой.
Более подробно можно посмотреть в RamblerConferences.
Здравствуйте! Большое вам спасибо за то что вы делаете!
У меня есть вопрос по поводу передачи данных в другой модуль на Swift. Поле moduleInput у протокола ModuleInputProvider это класс реализующий протокол ModuleInput? И не совсем понимаю что такое блок конфигурации. Заранее спасибо!
Добрый день. Возник такой вопрос: обратил внимание что в вашем приложении https://github.com/rambler-digital-solutions/rambler-it-ios ссылка на другие сториборды часто (может и всегда, все сториборды не проверял) идет через идентификатор контроллера, а не через установку у одного из контроллеров своства is initial view controller, даже если других точек входа в сториборду нет (например, в Report.storyboard только два вьюконтроллера и только у одного из них есть идентификатор). Хотелось бы уточнить - дело только в личных предпочтениях или же есть какая-то иная причина, которую я не понял?
@aknew Как я понял, так просто удобнее. В твоем примере ссылка на этот контроллер идет из Main.storyboard. Гораздо легче из роутера просто дернуть segue и uikit сделает все за тебя (инициализирует контроллер из нужного сториборда), чем самому указывать сториборд и вызывать у наго instantiateInitialViewController. Т.е. из кода нам даже не нужно знать, что нужный нам контроллер находиться в другом сториборде, мы просто дергаем segue. P.S. я не из Rambler'a. Возможно, у ребят был какой-то другой мотив.
Ты не понял, вопрос не про seque как подход - с ним все понятно. Вопрос в том что ссылку на контроллер в другой сториборде можно установить двумя способами:
@aknew Спасибо за вопрос! Как раз таки в основном из-за того, что может быть несколько точек входа в сториборду и лучше в явном виде указывать, на какой экран мы хотим перейти.
Спасибо за ответ
Здравствуйте,
Меня интересует вопрос по архитектуре. Приведу пример, допустим у меня в приложении есть какой-то сервис закачек файлов, который может работать в фоне. Есть какой-то модуль, который использует данные и отображает количество загруженых файлов и тп. Так вот, какой компонент модуля (презентер или интерактор) держит ссылку на сервис и получает данные от него? Мне кажется, что если добавить ссылку на сервис в интерактор - то это добавляет избыточности, порождает дополнительные пробросы Service->Interactor->Presenter. Но если я правильно понимаю, презентер не должен держать ссылку на сервис.
Как вы поступаете в этом случае?
@igorkotkovets Приветствую! Стандартное решение - интерактор, именно он ответственен за взаимодействие с сервисным слоем, соответственно он и держит ссылку на сервис.
В случае, если разрешить подключение сервиса в Presenter в данном случае, то может возникнуть желание делать это и в дальнейшем. Происходит смешение ответственностей P и I, чего допускать не стоит.
@DevAlloy спасибо за ответ!
Добрый день. Есть несколько вопроса:
Спасибо, но это не совсем то - по ссылке описано как использовать objc-протокол из ViperMcFlurry (я кстати так уже сделал к моменту написания вопроса), но вот тут https://www.youtube.com/watch?v=m4MYKzlqtH8 утверждалось вроде что можно сделать через extantions. Впрочем, вопрос еще и в том что ни один из этих способов, по-видимому, не заложен в темплейт swifty_viper
@aknew В видео говорилось про расширения протоколов. Тут есть реализация переходов между модулями с помощью transition handler-а, но пока без передачи данных.
День добрый. Как правильно реализовать левое меню через 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) Что-то лучше?
Первый вариант самый предпочтительный на мой вгляд
@DevAlloy благодарю.
Здравствуйте, как автоматизировать весь процесс в app extension (виджет), там нет app delegate(как добавить rambler assembly collector?), модуль из сториборда автоматически не собирается...
Здравствуйте. Если возможно очень хотелось бы услышать несколько доводов в пользу использования Storyboard-ов в контексте VIPER или отказа от них. Многие отказываются совсем от Storyboard-ов, приводя как аргумент, что их неудобно мерджить и удобнее в одном месте контроллировать изменения внутренностей вью контроллера. Есть ли примеры практик имплементации VIPER архитектуры в приложении без применения Storyboard? Может быть у Вас был опыт и Вы отказались в пользу использования Storyboard-ов по каким то причинам?
Спасибо за ответ
@dst-bengus мы с самого начала использовали VIPER со сторибордами на всех проектах, поскольку "проблемы" при работе с ними нам кажутся надуманными. Если это не проект размером с Facebook, конечно. Поэтому опыта работа без сторибордов особо нет.
@serkrapiv Спасибо за ответ. Тогда вопрос сформулирую по другому, хотя оффтоп получается. Какие откровенные преимущества Вашей команде дает использование Storyboard, кроме НЕпринесения "проблем"? Интересно услышать мнение команды, работающей в количестве >3 над проектами не из одного экрана.
@dst-bengus Привет! Я тут со своим самоваром влезу ) Если говорить про преимущества, то в Storyboard удобно смотреть из чего собирается экран, когда он модульный. Особенно если активно используется IBDesignable. Ну и переходы на другие экраны так смотреть удобно, если их не очень много. Также стоит активно использовать референсы и бить сториборды по Userstory. Условно процесс покупки в одном месте, поиск в другом и т.п. Особенно при работе в команде. Тогда будет меньше конфликтов, борды будут быстрее открываться и не будет паутины из переходов.
И ещё, если приложение активно поддерживает диплинки, которые могут открывать очень глубокие части приложения, формировать историю просмотра, либо поддерживает State Restoration, переходы между экранами и наполнение их модулями лучше делать без Segue. Это позволит создавать экраны синхронно, с большой вложенностью и простой настройкой. Так удобнее и быстрее. При таком подходе от Storyboard остается по сути вместилище разметки для контроллеров. Если она простая, то действительно стоит совсем от Storyboard отказаться.
@AndreyZarembo Спасибо. Как раз примеры переходов между модулями без Segue и разыскиваю вменяемые. Потому, что испытываю личную неприязнь к Storyboard)
Самый верный способ - взять чистый проект и написать верхнеуровнево от души, чтобы было удобно ) А потом под это написать нижний слой с нуля или на базе чего-то готового.
@AndreyZarembo Спасибо еще раз
@dst-bengus я как раз из тех сторонников не использования сторибордов. Так что все сториборды заменены на xib'ы. Главный плюс: можно использовать кастомные иниты, в которые можно прокидывать зависимости.
@ManWithBear свой человек! Но я иду дальше и интерфейс билдер вообще не использую. гибче получается. Даешь весь UI из кода.
Вопрос по работе с moduleOutput. Кто держит этот объект? И кто вызывает его методы? Первоначально он приходит в презентер, раз в RamblerViperModuleInput
есть - (void)setModuleOutput:(id<RamblerViperModuleOutput>)moduleOutput;
Смотрим дальше. По-сути, это протокол делегата модуля. В этом случае первым параметром должен идти вызывающий объект, как например у - (void)scrollViewDidScroll:(UIScrollView *)scrollView;
. Т.е в нашем случае передаваться должен viewController? Если держать moduleOutput в презентере и из него вызывать методы moduleOutput, то мы не сможем передать его, т.к view закрыт протоколом. Тут в примере методов: https://etolstoy.gitbooks.io/the-book-of-viper/content/%D0%B2%D0%BE%D0%BF%D1%80%D0%BE%D1%81%D1%8B-code-style.html вообще опущен этот параметр.
@nemissm в наших проектах moduleOutput держался презентером дочернего модуля. Этот самый презентер и вызывал методы moduleOutput при необходимости.
Что касается передачи вызывающего объекта первым параметром - да, по канонам здесь первым параметром надо передавать презентер дочернего модуля (в виде id
Привет. Прошу разрешить давний спор касательно view-слоя. В книге сказано
Он может кешировать некоторые данные для быстрого доступа, валидировать их, но его основная обязанность - гарантировать, что view отображает правильную информацию.
Предположим, модуль представляет из себя таблицу с однотипными данными. Правильно ли я понимаю под кэшированием хранение пассивных моделей во view для установки значений аутлетов этих ячеек в cellForRowAtIndexPath
-методе?
@sdanny да
@Brain89 ребята, раскройте пожалуйста вопрос более широко,
"Не храните во view controller данные. View controller выступает посредником между модельным слоем и слоем представления при обмене данными. Он может кешировать некоторые данные для быстрого доступа, валидировать их, но его основная обязанность - гарантировать, что view отображает правильную информацию."
На сколько я понимаю из этого контекста, что мы можем кэшировать фотографии к примеру, для быстрого их доступа. Но никак не хранить во вьюслое ВьюМодели! Давайте представим что у нас есть мессенджер, где мы храним вьюмодель для таблицы ? (где мы храним все данные ячеек). В презентере или во вьюконтроллере? Иначе из вашего ответа у людей возникает ощущение, что мы можем просто держать модель ячеек сообщения во вьюконтроллере
Ребята, вы очень больше дело делаете, спасибо! У меня есть несколько вопросов, некоторые из которых я думаю будет полезно осветить в том или ином виде в книге про VIPER (если просто ответите в тикете тоже буду благодарен)