Как стать автором
Обновить
15
0
Кирилл @ws233

Руководитель мобильной разработки

Отправить сообщение

Что будет с тестом, если функция loader.loadMovies() подвиснет?

Интересно, что "двунаправленная архитектура" легко преобразуется в "однонаправленную". Достаточно в схеме "двунаправленной архитектуры" тот блок, что между двумя оставшимися находится, поделить на 2 части: одна часть пропускает сигналы в одну сторону, вторая – в противоположную. да, получится 4 блока, а не 3, но смысл тот же. И описанное преобразование гораздо проще, чем переписывать какой-нить MVVM на MVI. Ну, и плюшки все получаете от однонаправленности в том же MVVM.

Только тсссс!

IMHO, как раз вот не все из функций контроллера SwiftUI умеет делать хорошо. Есть вещи, которые как раз существенно лучше реализуются через механизмы уже имеющиеся в контроллерах, но про которые разработчики почему-то забыли и пытаются их костылить в обертке SwiftUI. Но навязывать свое мнение не стану. В остальном согласен. Обёрточка вышла годной. Просто прошу не забывать (а если не знали, то узнать) о том, что под ней и что и так работает прекрасно.

Красиво пишите. Еще и ссылок много – видно, что стараетесь базой подтверждать каждую свою мысль. Продолжайте, таких авторов в иосе немного.

Разрешите добавлю по теме статьи.

Паттерны в SwiftUI не упоминаются, кмк, по одной простой причине – изменений нет. Это все тот же MVC. Но теперь это MVC в стандартной реализации, а не в модифицированной версии Cocoa от Apple.

Поясню.

Все знают MVC в виде с рисунка 7.2 (как раз модификация от Apple), но мало кто знает оригинальную модификацию с рисунка 7.1. Так вот, если Вы возьмете вашу картинку Архитектуру State-Model-View из статьи и дорисуете к ней сверху контроллер, как медиатор между View и Model, то Вы получите полностью канонический MVC с картинки 7.1. А в описании увидите и про биндинги, и про состояние.

"Но в SwiftUI нет контроллеров", – возразите Вы.

На что я отвечу, что они есть, просто Apple сделала их полностью абстрактными и скрытыми от программиста, переместив все, что раньше размещали в контроллере, на вью SwiftUI. Поэтому какой бы экран вы не верстали на SwiftUI, контроллер, рисующий экран, у вас все равно будет. Полностью абстрактный.

Отсюда, кстати, и очень важные следствия:

  1. А все ли из того, что традиционно размещают в контроллере возможно перенести на вью?

  2. Подсказка. Обратите внимание, какие взаимодействия идут напрямую от View к Model в MVC, а какие через Controller.

  3. А если что-то переносить не стоит, то как использовать SwiftUI с кастомизированным, а не абстрактным контроллером? А это возможно.

  4. А стоило ли Apple делать полностью абстрактный контроллер и прятать его от разработчиков? Не приведет ли это к путанице и новым связностям? – Подсказка: уже приводит.

  5. В итоге, глубокое понимание того, что должно быть в контроллере, а что во вью, кажется, не даст испортить подход на SwiftUI и повторить уже классические ошибки с MassiveSwiftUI. Вы правильно указали, что проблема MassiveViewController легко решается. Так вот сейчас сообществу необходимо будет понять эту разницу, чтобы не испортить всю идею MassiveSwiftUI подходом. Что-то из этого Вы уже тоже предложили.

Ваше мнение?

ПС: Apple еще и Coordinating Controller описывает, который очень перекликается с комбинациями визуальных элементов ("Combine" pattern) и управлением переходами, которые вы описываете.

Зависимости в коде бывают ооооочень разными.

Все публикуемые примеры архитектур обычно исправляют только самые очевидные зависимости и пропускают менее очевидные неявные, при этом делают иногда еще хуже.

Конкретно в обоих этих примерах не устранена скрытая контекстная зависимость. ViewModel вынуждена знать и говорить на языке View: все эти методы "didSearch", "didSelect", "viewDidLoad" и замыкания (названные в статье "кложурами") для вызова View кода из ViewModel прям кричат о том, что они опустились туда, куда не должны были.

Это приводит к нарушению единой ответственности - при изменении View, приходится менять и вьюмодель вот из-за таких неявных контекстных зависимостей.

Также теряется возможность переиспользовать вьюмодель на других платформах, где логика отображения будет другой и не будет иметь метода viewDidLoad (т.е. мы в нашу вьюмодель внесли зависимость от платформы, под которую написан UI). Например, будет тяжело переиспользовать эту ViewModel в консольном приложении.

Исправляется довольно просто. В обоих случаях ViewModel становится свободной о знании деталей реализации View.

  1. Из ViewModel удаляются замыкания. ViewModel просто возвращает управление вьюхе из той функции, где вызывалось замыкание, а уже View коммуницирует с координатором и осуществляет нужный переход.

  2. С функциями типа "viewDidLoad" аналогично. Скорее всего эта функция должна быть во вьюхе и напрямую вызывать смысловые методы вьюмодели, которые уже объявлены в интерфейсе.

Также имеет смысл почитать, а что же такое замыкание и для чего оно должно быть использовано. Использование замыканий не по назначению – все равно, что саморезы в бетон молотком забивать – только хуже станет, даже если кажется, что будет лучше.

В остальном – статья глубокая. Автор молодец. Переводчик тоже – добро пожаловать в расширенный аккаунт на Хабре :)

Не соглашусь с тем, что он приведен "чисто для примера". Но соглашусь с тем, что он "формирует запросы в интерактор".

Контроллер у дядюшки Боба (да и не только у него, это часто встречающаяся практика) осуществляет ту же функцию, что и презентер, только в обратную сторону. Контроллер и презентер у дяди Боба – это прослойка между двумя независимыми слоями, разделенная по направлению преобразования на независимые сущности. Просто гляньте где у него в кольце контроллер, а где презентер. Контроллер обычно работает с вводом пользователя, а презентер – наоборот – с выводом ему информации. Это реализация принципа инверсии зависимостей, порт-адаптер (преобразование) и единой ответственности (направление). Не зря на картинке и то, и другое отображено одинаково, только с разным направлением управляющего сигнала.

Без такого адаптера "Вид" в VIP будет все знать о бизнес-логике, а значит, и на идее независимости слоев, и на идее повторного переиспользования (на которых построена вся книга), можно ставить крест. В направлении от "Вида" к бизнес логике VIP потерял промежуточное звено, потерял адаптер, приобрел жесткую зависимость "Вида" от бизнес-логики.

Не утверждаю, что это неверно и неправильно.

Просто, похоже, что VIP – это не "чистая архитектура" от дяди Боба. В чистой архитектуре вот к той сноске в правом нижнем углу нужно просто слева подрисовать представление ("Вид") и замкнуть граф в кольцо.

Вы не считаете это ошибкой реализации VIP относительно описанных у дяди Боба колец с адаптерами?

У дядюшки Боба в его чистой архитектуре присутствует контроллер. Поглядите хотя бы на ту картинку, которую Вы привели в своей статье. В ее правом нижнем углу имеется контроллер. А вот куда контроллер делся в VIP, который якобы является реализацией чистой архитектуры с этой картинки? И почему Вы продолжаете с этой же ошибкой в Вашей архитектуре?

Потому что, как выше написали, абстракции нужны НЕ для переиспользования кода. Протокол прекрасно выполняет роль абстракции. Точка. Для этого создавать еще и абстрактные классы совсем нет необходимости.

Для переиспользования кода нужно использовать другие инструменты.

Автору:

Не пытайтесь забивать гвозди отверткой.

За системный подход к деталям Вам – огромный плюс. Статья получилась глубокая.

А вот за то, что утонули в этих деталях, забыли про взгляд с высоты птичьего полета и начали применять инструмент не по назначению, хотя рядом лежит тот инструмент, что Вам нужен – минус. Попробуйте прикинуть, какими еще инструментами надо получать переиспользование кода и сравните, что получится – вот это будет интересная и полезная для сообщества статья.

Спасибо за Ваши усилия.

Очень опасная обертка.

  1. 2 объекта одного класса не будут синхронизированы между собой.

  2. Нет потокобезопасности.

Вывод: Использовать обертку можно только с синглтонами.

Но об этом ни в статье, ни в описании кода обертки – ни слова.

Исключение, лишь подтверждающее правило?

Значит, им там и место :)

А больше нигде и не нужно. И все попытки натянуть сову на глобус – неправильны в самой постановке реализации.

Либо пользуемся имеющимися контроллерами, либо создаем свой универсальный... Но мне сложно придумать такой универсальный контроллер, в котором мне бы пришлось добавлять поддержку работы с клавиатурой. Имеющихся двух вполне достаточно для решения проблемы, описанной в статье. Да и любой другой проблемы с клавиатурой.

Более того, это поведение зашито в UITableViewController и работает из коробки. Если поместить UITextField в ячейку таблички, то даже строчки кода писать не придется – все взлетит, как есть.

Могу ошибаться, но это же поведение зашито и в UIScrollView. UITextView содержит в себе UIScrollView, а значит, что и в нем это поведение должно быть из коробки. Но если его вдруг нет, то лучше вешать contentInset на keyboardLayoutGuide, а не позицию компонента на экране.

Профиты "зашитого" поведения:

  1. Не придется тратить время на написание того, что уже работает.

  2. Не придется писать тесты своих велосипедов.

  3. При последующих изменениях в ядре системы можно быть уверенным, что Apple о нас подумает и добавить поддержку старого кода и поведения.

Цикл статей о той же идее, но со слегка отличной реализацией.

В частности тут про источник данных. Но есть там статьи и про применение этой же идеи и к делегатам.

Минусы в Вашей реализации имеются. Попробую бегло перечислить, Вы можете сами более детально сравнить с тем, что описывается у нас и сделать свои выводы.

  1. Вы сделали жесткое соответствие типа вьюмодели и типа вьюхи, как один к одному. Этим Вы внесли зависимость вьюхи (представления) от вьюмодели (данных). И наоборот: вьюмодель должна знать о типе ячейки, где она будет отображаться (`$0.base`). Не зря все архитектуры стараются строить на трех элементах, где средний – это прослойка, необходимая для того, чтобы убрать зависимость как представления от данных, так и данных от представления. Углубляться, какие проблемы тут будут не стану, скажу лишь, что добавить такое разделение не сложно. В итоге вы получите связь между вью и вьюмоделью – многие ко многим, что гораздо гибче и удобнее в использовании.

  2. Вы явно не указали, что такое `itemRepresentation: { $0.base }`. Но я предположу, что это некое занесенное во вьюмодель знание о вьюхе, на которую эту вьюмодель надо смэппить. Это снова нарушение принципа из пункта 1. Сложно с этим будет и так лучше не делать. Вам это знание нужно из вьюмодели выносить. Вся информация и все алгоритмы, отвечающие за соответствие должны находиться в одном месте – мы этот класс назвали картой соответствия. Создавая разные карты, Вы можете реализовывать разные алгоритмы соответствия. Можно – класс на класс, можно объект – на класс, объект – на объект, класс – на объект, можно вешать сложную логику по выбору объекта вьюхи или ее класса через замыкания. И все это будет отдельно – менять ни вью, ни вьюмодель не придется. У вас же вся эта ответственность, которую Вы называете ViewRegistration, размазана по многим классам (при необходимости изменения логики соответствия придется менять все перечисленные классы, а их будет ооооочень много):

    1. регистрация – в источнике данных;

    2. идентификатор соответствия – во вьюмодели;

    3. замыкание выбора соответствия – вообще по коду непонятно где. Но я предположу, что в контроллере.

  3. Не смешивайте регистрацию типа ячейки в таблице и регистрацию вьюмодели на ячейку. Это снова нарушение п.1. И это вам не дает видеть и понимать ясно разные ответственности уровня вью, уровня вьюмодели и промежуточного уровня между ними, который осуществляет мэппинг одного в другое без жесткой связи между ними.
    Отсутствие жесткой связи в этом месте очень важно. Регистрируйте отдельно. Хоть это и лишний код, но Вы сможете переиспользовать одну таблицу со всеми зарегистрированными ячейками на всех своих экранах. Кажется, что экономия тут больше, чем в Вашем случае. Не настаиваю.
    При этом обратите внимание, что Вы чувствуете, что регистрация, это некая ответственность, которую необходимо инкапсулировать. Но инкапсулируете Вы 2 куска разных ответственностей, при этом оставляя куски одной из них размазанными по другим классам и другим ответственностям. Это отсылка к п.2.

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

Почему же... Пример.

Но ФСБ со своими законами - все же регламент. Поэтому и делаем.

Да и наши безопасники считают, что разобрать то, что написано в гугле не каждый сможет. Вон ниже ссылка про защиту SnapChat, если я правильно ее понял при беглом просмотре, то товарищу за 2 недели так и не удалось вскрыть SnapChat. Та самая неприемлемая стоимость вскрытия. А напихать сотни проверок - не такая уж и трудоемкая задача-то...

Так знаем же. И защищаемся и от дебага тоже. Еще раз – полностью защититься нельзя. Можно лишь поднять стоимость взлома до неинтересных значений.

Или Вы предлагаете и стоимость взлома не повышать на одном лишь утверждении, что защититься нельзя? – пусть ломают простые смертные в 2 клика мышкой?

Что Вы-то предлагаете?

Так "прикладники" все прекрасно понимают. Взломать можно все. Защититься невозможно. Но можно сделать взлом экономически нецелесообразным. Это прикладникам доступно или тоже нет?

Ковырять приложение из под OS существенно дороже, чем напрямую само приложение. Поэтому и ковыряют приложение. И это надо сделать экономически нецелесообразным, чтобы сваливались на уровень OS, и пусть там творят, че хотят. На это мы-"прикладники" уже повлиять не можем. Но на свою часть повлиять можем. И будем.

Или у Вас другое мнение?

Тоже добавлю, что результат перечисленных проверок на взломанном устройстве тупо подменится злоумышленником на необходимый. И приложение, получив ожидаемый результат, даже не увидит, что запущено на взломанном устройстве, и продолжит работать, как в безопасной среде со всеми вытекающими.

Этих проверок мало, нужна еще как минимум защита от дебага.

И да, полностью защититься от взлома невозможно. Можно лишь так усложнить взлом, чтобы его стоимость стала дороже, чем возможный выигрыш от взлома. Таким образом Вы просто делаете взлом экономически нецелесообразным.

Если взять во внимание этот простой тезис, то становится вообще непонятно, почему люди публикуют проверки безопасности под тезисом: "Мы настолько уверены в своей защите, что готовы показать ее всем!". Кто-то сможет пояснить о чем, кроме пиара думают эти люди?

Автор – молодец! Копай глубже!

А к Qiwi теперь вопросики...

Спасибо за статью. Качественно и полезно.

Ждем "отдельный доклад или статью" о том, как фильтровать и группировать результаты производительности запуска экранов. Без них данная статья выглядит неполной.

Тестовый стенд один.

Разработческих – много.

Разработка проводится на командном разработческом стенде, поэтому влияния команд друг на друга при разработке нет.

Тестовый стенд подключается к делу на этапе, когда разработка уже закончена, весь код разных команд собран вместе. Этот стенд – как раз для проверки интеграций и взаимовлияний.

Такой подход позволяет упростить тестирование и поиск ошибок.

Кстати, стоит добавить, что качество ПО вполне себе измеряемая величина. Для ее измерения существует подход, называемый мутационным тестированием. Он как раз и считает сколько раз в вашем коде можно ошибиться, например, инвертировав условие в операторе ветвления, и сколько из этих ошибок мы отлавливаем тестами.

Зная, общее число ошибок, мы можем проверить, реагирует ли хоть один тест на каждую из имеющихся ошибок. Если реагирует, то мы защищены от такой ошибки. Отношение ошибок, от которых мы защищены, к общему числу ошибок и дает нам качественный показатель нашего ПО.

1
23 ...

Информация

В рейтинге
Не участвует
Откуда
Москва, Москва и Московская обл., Россия
Работает в
Дата рождения
Зарегистрирован
Активность