и узнайте, как писать меньше кода с протоколами Swift

Говорят, хороший программист пишет 100 строк кода в день, а отличный удаляет 50. Согласны? Давайте станем последним, следуя принципу «Не повторяйся», потому что лучший код - это вообще не код 😉

Повторяйте функции, а не код

При разработке программного обеспечения распространенный сценарий заключается в размещении одинаковых функций в нескольких местах проекта. Большинство приложений iOS состоит из нескольких экранов, каждый из которых является отдельным UIViewController. А теперь представьте, что ваш product owner приходит и говорит:

Мы должны сообщать пользователю об ошибках API! Давайте просто представим оповещение, если запрос не выполнен.

Это просто! Все, что вам нужно, это небольшая функция в вашем контроллере представления:

Теперь ваш LoginViewController знает, как отображать предупреждение об ошибке. Но послушайте, логин - не единственное место, где ваше приложение отправляет запросы API, верно? Взяв в качестве примера наш последний продукт - RocketLuncher, есть также экран регистрации, меню, сводка заказа, изменение пароля и так далее. Каждый требует обработки ошибок. Вставка одного и того же метода во все эти классы приводит к росту вашей кодовой базы и нарушает священный принцип СУХОЙ. Кроме того, будущие изменения вызывают проблемы, потому что вы должны применять их в нескольких местах.

Как избежать дублирования кода?

Первое, что может прийти в голову, это добавить расширение к классу UIViewController:

Плюсы: это просто, и функция мгновенно доступна во всех ваших контроллерах просмотра. Минусы? Что ж, эта функция доступна во всех ваших контроллерах представления, даже если она вам не нужна. Всегда лучше контролировать область доступа вашего кода.

А как насчет подклассов?

Поскольку мы хотим, чтобы функция presentError была доступна только в определенных классах, давайте следовать парадигме объектно-ориентированного программирования (ООП) и создать подкласс UIViewController. Мы можем использовать его везде, где хотим представить ошибки:

Так-то лучше! Мы не только избавились от дублирования кода, но и наша функция доступна только там, где мы явно выбрали это. Однако с этим решением есть большая проблема. Представьте, что вы хотите, чтобы на некоторых экранах также отображалось UIActivityIndicator. По тому же пути вы создаете ActivityIndicatorViewController с функцией presentIndicator. Swift не поддерживает множественное наследование, поэтому вы не можете создать класс, который одновременно является ErrorPresentingViewController и ActivityIndicatorViewController подклассом. Мы могли бы добавить новый ErrorPresentingAndActivityIndicatorViewController, но это даже ужасно звучит 😤 Очевидно, мы достигли пределов ООП. Что теперь?

Давайте использовать протоколы!

Согласно удивительному докладу WWDC 2015 Дэйва Абрахамса, Swift - это язык программирования, ориентированного на протокол (POP). Давайте заглянем в документацию Swift, чтобы узнать о протоколах:

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

Как мы можем использовать это для решения нашей проблемы? Сначала объявим протокол:

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

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

Это именно то, что нам нужно. Давайте код!

Создавая расширение для ErrorPresenting протокола, все соответствующие типы автоматически получают presentError реализацию метода без каких-либо дополнительных изменений. Строка уведомления 5: where Self: UIViewController. Когда вы определяете расширение протокола, вы можете указать ограничения, которым должны удовлетворять соответствующие типы, прежде чем будут доступны методы и свойства расширения. В этом случае мы хотим представить предупреждение, поэтому нам нужна present функция, которая доступна в UIViewController.

Все, что осталось сделать, это сделать так, чтобы каждый контроллер представления, требующий обработки ошибок, соответствовал ErrorPresenting:

Теперь вы можете использовать presentError во всех и только тех контроллерах представления, которые соответствуют протоколу - вы полностью контролируете, кто может получить доступ к вашему методу. Также отсутствует дублирование кода: presentError реализуется только один раз в расширении протокола. Поздравляю, вы только что научились писать меньше кода ☺️

РЕДАКТИРОВАТЬ:
Прочтите продолжение статьи Человеку свойственно ошибаться, но как это показать? об удобном способе отображения ошибок в приложениях iOS.

Не забудьте нажать и удерживать 👏 и подписаться на Блог DaftMobile (просто нажмите кнопку Подписаться под 😅)! Вы также можете найти нас в Facebook и Twitter 🙂