А пока можно, например, пофантазировать, как бы этот код мог выглядеть (там, правда, все довольно просто), попробовать перо в собственном гитхабе, поделиться ссылками на такие пробы, обсудить, а потом сравнить с тем, что получится у нас. С радостью приглашаем к диалогу! :)
Вот хорошая статья для ознакомления, как можно сделать неплохой наблюдатель на Свифт: https://www.swiftbysundell.com/articles/first-class-functions-in-swift/. Удовлетворяет всем критериям, что вы обозначили? Проведете сравнение вместе с остальными нативными реализациями наблюдателя и добавите в статью, чтобы она получилась всеобъемлющей?
А еще кажется, что KVO, property wrappers и даже observer blocks (те самые didSet, willSet) тоже являются различными реализациями паттерна наблюдатель, но в статье они не рассмотрены даже бегло, как и реализации в Combine.
Очень даже обоснованный. Responder chain, как раз то, что делает addTarget: таким мощным средством. Как выглядит типичный роутинг в 90% iOS приложений? Правильно: одни и те же экшены в каждом контроллере, из которого необходимо открывать, например, одно и то же модальное окно. Теперь как это можно улучшить воспользовавшись Responder chain: 1. Оставляем лишь один action 2. Переносим его в контейнер контроллер, например в таббар-контроллер. 3. В addTarget указываем nil вместо конкретного контроллера, в котором находится UI-элемент, к которому подключается подписка. 4. В принципе все. Из-за того, что мы не указали конкретный таргет, отправка нотификации уходит по Responder chain и доходит от каждого конкретного контроллера до таббар-контроллера и показывается то самое необходимое модальное окно. Только action теперь в едином экземпляре со всеми вытекающими. Данный подход перенесен на iOS из Mac, в котором он использовался для работы с командами меню. Если проследить аналогии, то понятно, что команда должна быть одна, а вот вызываться она может, когда совершенно разные UI-элементы и целые окна могут быть активны. Именно этим и надо пользоваться в iOS.
Как сделать то же самое с вашим универсальным наблюдателем? Именно поэтому я и сделал акцент на том, что каждый инструмент должен применяться в тех случаях, под которые он заточен.
Спасибо за статью. Качественно. Только, наверное, стоит отметить в статье, что каждый инструмент нужно использовать по назначению, а не пробовать построить универсальный молоток под саморезы и шурупы. В частности, необходимо отметить, что тот же `addTarget:` задумывался под конкретную цель. А именно -- работу с responder chain. Попытки заменить `addTarget:` на ваш "идеальный наблюдатель" приведут к необходимости дублировать экшены из контроллера в контроллер, из вьюхи во вьюху или же добавят сильной связности между ними, если вдруг вы попытаетесь не дублировать экшены, а прокидывать их между перечисленными классами.
Модель ячейки контейнера знает о своей ячейке и какой в нее помещают контент.
Но ведь слоистая архитектура... Модель вложена в контент, вложенный в ячейку. А значит модель, не имеет права знать ни о контент-вьюхе, ни о ячейке.
То же самое относится к размерам, инсетам и (О! Боже!) обработчикам действий пользователей. Даже количество линий в метке и то моделью задается. Вы опустили на уровень ниже ответственности более высокого уровня. `UIEdgeInsets` не зря в ObjC имел префикс UI. Сразу было понятно, что не стоит эту штуку опускать ниже. То же самое касается даже числа линий в метке при отображении текста. Вьюха. Вьюха эти параметры определяет. Возьмите другую вьюху с другими настройками и подставьте ее вместо текущей, и вам не придется менять модель. Совсем. И не придется писать код, проксирующий настройки из модели во вьюху. Совсем. Это ведь бесполезный код. Лишний, вредный.
Именно поэтому вся ваша история и несовместима с InterfaceBuilder.
Если поднимите назад на уровень выше все вещи слоя представления, то ваша схема прекрасно будет работать даже с ячейками, сверстанными в IB. По крайней мере у нас работает.
Для настройки вьюх поглядите паттерн декоратор. Все, что вы опустили в модельку, разместиться в декораторе, располагающемся на уровне представления.
Бонусом вы потеряете жесткую связь 1 к 1 от вьюхи к ее модели и сможете оперировать протоколами, вешая один и тот же протокол, необходимый вьюхе, на разные модельки, что даст вам большую гибкость при работе с данными и отменит жесткий меппинг из одних структур в другие.
Ну и мелочь: `ConteinerItem` -> `ContainerItem` все же.
А пока можно, например, пофантазировать, как бы этот код мог выглядеть (там, правда, все довольно просто), попробовать перо в собственном гитхабе, поделиться ссылками на такие пробы, обсудить, а потом сравнить с тем, что получится у нас. С радостью приглашаем к диалогу! :)
Вот хорошая статья для ознакомления, как можно сделать неплохой наблюдатель на Свифт: https://www.swiftbysundell.com/articles/first-class-functions-in-swift/. Удовлетворяет всем критериям, что вы обозначили? Проведете сравнение вместе с остальными нативными реализациями наблюдателя и добавите в статью, чтобы она получилась всеобъемлющей?
А еще кажется, что KVO, property wrappers и даже observer blocks (те самые didSet, willSet) тоже являются различными реализациями паттерна наблюдатель, но в статье они не рассмотрены даже бегло, как и реализации в Combine.
Очень даже обоснованный. Responder chain, как раз то, что делает addTarget: таким мощным средством. Как выглядит типичный роутинг в 90% iOS приложений? Правильно: одни и те же экшены в каждом контроллере, из которого необходимо открывать, например, одно и то же модальное окно. Теперь как это можно улучшить воспользовавшись Responder chain:
1. Оставляем лишь один action
2. Переносим его в контейнер контроллер, например в таббар-контроллер.
3. В addTarget указываем nil вместо конкретного контроллера, в котором находится UI-элемент, к которому подключается подписка.
4. В принципе все. Из-за того, что мы не указали конкретный таргет, отправка нотификации уходит по Responder chain и доходит от каждого конкретного контроллера до таббар-контроллера и показывается то самое необходимое модальное окно. Только action теперь в едином экземпляре со всеми вытекающими. Данный подход перенесен на iOS из Mac, в котором он использовался для работы с командами меню. Если проследить аналогии, то понятно, что команда должна быть одна, а вот вызываться она может, когда совершенно разные UI-элементы и целые окна могут быть активны. Именно этим и надо пользоваться в iOS.
Как сделать то же самое с вашим универсальным наблюдателем? Именно поэтому я и сделал акцент на том, что каждый инструмент должен применяться в тех случаях, под которые он заточен.
Спасибо за статью. Качественно.
Только, наверное, стоит отметить в статье, что каждый инструмент нужно использовать по назначению, а не пробовать построить универсальный молоток под саморезы и шурупы.
В частности, необходимо отметить, что тот же `addTarget:` задумывался под конкретную цель. А именно -- работу с responder chain. Попытки заменить `addTarget:` на ваш "идеальный наблюдатель" приведут к необходимости дублировать экшены из контроллера в контроллер, из вьюхи во вьюху или же добавят сильной связности между ними, если вдруг вы попытаетесь не дублировать экшены, а прокидывать их между перечисленными классами.
Под M1 далеко не каждый проект соберется, а если и соберется, то не запустятся тесты. Мы до ранера даже не дошли :)
Но ведь слоистая архитектура... Модель вложена в контент, вложенный в ячейку. А значит модель, не имеет права знать ни о контент-вьюхе, ни о ячейке.
То же самое относится к размерам, инсетам и (О! Боже!) обработчикам действий пользователей. Даже количество линий в метке и то моделью задается. Вы опустили на уровень ниже ответственности более высокого уровня. `UIEdgeInsets` не зря в ObjC имел префикс UI. Сразу было понятно, что не стоит эту штуку опускать ниже. То же самое касается даже числа линий в метке при отображении текста. Вьюха. Вьюха эти параметры определяет. Возьмите другую вьюху с другими настройками и подставьте ее вместо текущей, и вам не придется менять модель. Совсем. И не придется писать код, проксирующий настройки из модели во вьюху. Совсем. Это ведь бесполезный код. Лишний, вредный.
Именно поэтому вся ваша история и несовместима с InterfaceBuilder.
Если поднимите назад на уровень выше все вещи слоя представления, то ваша схема прекрасно будет работать даже с ячейками, сверстанными в IB. По крайней мере у нас работает.
Для настройки вьюх поглядите паттерн декоратор. Все, что вы опустили в модельку, разместиться в декораторе, располагающемся на уровне представления.
Бонусом вы потеряете жесткую связь 1 к 1 от вьюхи к ее модели и сможете оперировать протоколами, вешая один и тот же протокол, необходимый вьюхе, на разные модельки, что даст вам большую гибкость при работе с данными и отменит жесткий меппинг из одних структур в другие.
Ну и мелочь: `ConteinerItem` -> `ContainerItem` все же.
typealias MyAbstractClass = MyAbstractClass & _MyAbstractProtocol
похоже, тут подчеркивание потеряно? очепятко?