Как создать мосты между фреймворками в приложении для iOS

Если код вашего приложения выглядит следующим образом ...

«Я хочу экспортировать эту часть своего приложения, но она привязана к остальной части приложения, как тарелка спагетти!»

Попытка экспортировать небольшую часть приложения, которая слишком зависима

Когда я начинал модульную часть приложения, над которым работал, я наткнулся на стену.

Я хотел экспортировать службу (фактически это была служба отслеживания) в отдельную структуру. Проблема заключалась в том, что этот сервис был слишком сильно привязан к приложению. Он использовал другой сервис, который сам использовал другой, глубоко привязанный к приложению.

Чтобы экспортировать сервис отслеживания, мне пришлось бы провести рефакторинг и переделать весь набор сервисов в новом фреймворке!

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

Давайте начнем с конкретного примера!

Вот и мы, лучший способ учиться и понимать, как все работает, это практиковать! (Я собираюсь предоставить репозиторий Github для этого примера в конце этого поста)
Итак, позвольте мне установить контекст, у нас есть небольшое приложение только с двумя экранами:

  • Домашний экран
  • Экран оплаты (мы хотим экспортировать этот экран в каркас)

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

Главный экран и экран оплаты

Итак, у нас есть эти два экрана, объявленные в двух разных целях. Главный экран объявляется в основной цели приложения, а экран оплаты объявляется в другом модуле с именем PaymentModule. У нас также есть PaymentService, объявленный в основной цели приложения, как показано ниже:

Метод оплаты - это метод, который мы не можем извлечь из приложения, поскольку он слишком зависим. Но мы хотим использовать его из платежного модуля.

У нас есть PaymentViewController, определенный в модуле Payment. Если мы попытаемся вызвать PaymentService, у нас будет ошибка, так как этот сервис отсутствует в модуле. Вы не можете импортировать основную цель в модуле (это было бы бессмыслицей)

Итак, как мы собираемся использовать этот метод из PaymentViewController?

Определите протокол в модуле

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

Итак, давайте определим протокол с именем PaymentServiceProtocol с методом pay:

Реализация протокола в приложении

Теперь мы должны сообщить нашему платежному сервису о соответствии этому протоколу. Нам просто нужно добавить это:

«Почему метод, объявленный в протоколе, не реализован в этом расширении?»

Вы правы, когда вы соответствуете протоколу, вы должны реализовать его свойства и методы. Хитрость в том, что имя метода в протоколе точно совпадает с именем метода в PaymentService, которое мы объявили чуть ранее. Таким образом, система будет знать, что она будет использовать метод pay, объявленный в классе PaymentService, при доступе к методу протокола.

Связывание двух частей

Теперь мы должны объединить две части.
Из HomeViewController, когда мы нажимаем кнопку «Перейти на страницу оплаты», мы создаем экземпляр PaymentViewController. В то время мы собираемся передать ему ссылку на класс PaymentService, но контроллер платежей в модуле будет видеть его как тип PaymentServiceProtocol.

Вот хитрость:

Мы передаем PaymentService.self, и код в модуле видит PaymentServiceProtocol.Type.
Теперь мы можем использовать метод оплаты, определенный в приложении из модуля!

Используя мост

Теперь использовать мост, который мы создали, очень просто:

Метод didTapPayButton вызывается всякий раз, когда вы нажимаете на кнопку Pay (звучит правильно, верно?). Проверьте в строке 23: мы вызываем метод оплаты по ссылке на протокол, которую мы получили из приложения.

Поскольку PaymentService соответствует этому протоколу, система выполнит код внутри метода pay, который определен в PaymentService.swift.

Другими словами, мы используем метод, который мы не могли вызвать из модуля в начале! Мост сейчас установлен.

Вот как это выглядит, когда вы нажимаете на кнопку оплаты.

Используя метод оплаты, содержащийся в основной цели, из модуля оплаты

Вывод

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

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

Я думаю, что это временное решение, прежде чем вывести весь компонент внутри фреймворка, когда у вас будет время, например. (В этом случае когда-нибудь вам придется экспортировать метод оплаты внутри модуля Payment)

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

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

Эта история опубликована в журнале The Startup, крупнейшем издании по предпринимательству, за которым следят +442 678 человек.

Подпишитесь, чтобы получать наши главные истории здесь.