[Программирование, Разработка под iOS, Разработка мобильных приложений] SPM: модуляризация проекта для увеличения скорости сборки
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
Привет, Хабр! Меня зовут Эрик Басаргин, я iOS-разработчик в Surf.
На одном большом проекте мы столкнулись с низкой скоростью сборки — от трёх минут и более. Обычно в таких случаях студии практикуют модуляризацию проектов, чтобы не работать с огромными монолитами. Мы в Surf решили поэкспериментировать и модуляризовать проект с помощью Swift Package Manager — менеджера зависимостей от Apple.
О результатах расскажем в другой статье, а сейчас ответим на главные вопросы: зачем это всё нужно, почему мы выбрали SPM и как делали первые шаги.
Почему именно SPM
Ответ прост — это нативно и ново. Он не создает overhead в виде xcworkspace, как Cocoapods, к примеру. К тому же SPM — open-source проект, который активно развивается. Apple и сообщество исправляют в нем баги, устраняют уязвимости, обновляют вслед за Swift.
Делает ли это сборку проекта быстрее
Теоретически сборка ускорится из-за самого факта разделения приложения на модули — framework'и. Это значит, что каждый модуль будет собираться только в том случае, когда в него внесли изменения. Но утверждать точно можно будет только по окончанию эксперимента.
Note: Эффективность модуляризации напрямую зависит от правильного разбиения проекта на модули.
Как сделать эффективное разбиение
Метод разбиения зависит от выбранной архитектуры, типа приложения, его объема и планов на дальнейшее развитие. Поэтому я расскажу о трёх правилах, которых мы стараемся придерживаться при разбиении.
Разделять обобщённую функциональность. Каждый модуль отвечает за какую-то категорию, к примеру:
- CommonAssets — набор ваших Assets'ов и public интерфейс для доступа к ним. Обычно он генерируется с помощью SwiftGen.
- CommonExtensions — набор расширений, к примеру Foundation, UIKit, дополнительные зависимости.
Разделять flow'ы приложения. Рассмотрим древовидную структуру, где MainFlow — главное flow приложения. Представим, что у нас новостное приложение.
- NewFlow — экраны новостей и обзора конкретной новости.
- FavoritesFlow — экран со списком избранных новостей и экран обзора конкретной новости с дополнительным функционалом.
- SettingsFlow — экраны настроек приложения, аккаунта, категорий и т. д.
Выносить reusable компоненты в отдельные модули:
- CommonUIComponents — модуль, который содержит в себе любые небольшие UI-компоненты. Обычно они влезают в один файл.
- Далее список бесконечен и зависит от количества внутренних кастомных UI компонентов. К примеру, нужно создать модули для генерации экранов результата, кастомной коллекции, билдера кастомных алертов и т. д.
Когда нужно выносить компонент в отдельный модуль
Допустим, у нас есть конкретный flow, и мы хотим добавить в него новые функции. Если компонент будет переиспользоваться или это теоретически возможно в будущем — лучше вынести его в отдельный модуль или аналог CommonUIComponents. В других случаях можно оставить компонент локальным.
Такой подход решает проблему с потерей компонентов. Это происходит в больших проектах, и если компонент не задокументировали, то его поддержка и отладка впоследствии станет убыточной.
Создаём проект с использованием SPM
Рассмотрим создание тривиального тестового проекта. Я использую Multiplatform App project на SwiftUI. Платформа и интерфейс тут не имеют значения.
Note: Чтобы быстро создать Multiplatform App, нужен XCode 12.2 beta.
Создаём проект и видим следующее:
Теперь создадим первый модуль Common:
- добавляем папку Frameworks без создания директории;
- создаём SPM-пакет Common.
- Добавляем поддерживаемые платформы в файл Package.swift. У нас это platforms: [.iOS(.v14), .macOS(.v10_15)]
- Теперь добавляем наш модуль в каждый таргет. У нас это SPMExampleProject для iOS и SPMExampleProject для macOS.
Note: Достаточно подключать к таргетам только корневые модули. Они не добавлены как подмодули.
Подключение завершено. Теперь достаточно настроить модуль с public интерфейсом — и вуаля, первый модуль готов.
Как подключить зависимость у локального SPM-пакета
Добавим пакет AdditionalInfo — как Common, но без добавления к таргетам. Теперь изменим Package.swift у Common пакета.
Добавлять больше ничего не нужно. Можно использовать.
Пример, приближенный к реальности
Подключим к нашему тестовому проекту SwiftGen и добавим модуль Palette — он будет отвечать за доступ к палитре цветов, утвержденной дизайнером.
- Создаем новый корневой модуль по инструкции выше.
- Добавляем к нему корневые каталоги Scripts и Templates.
- Добавляем в корень модуля файл Palette.xcassets и пропишем какие-либо color set'ы.
- Добавляем пустой файл Palette.swift в Sources/Palette.
- Добавим в папку Templates шаблон palette.stencil.
- Теперь нужно прописать конфигурационный файл для SwiftGen. Для этого добавим файл swiftgen.yml в папку Scripts и пропишем в нем следующее:
xcassets:
inputs:
- ${SRCROOT}/Palette/Sources/Palette/Palette.xcassets
outputs:
- templatePath: ${SRCROOT}/Palette/Templates/palette.stencil
params:
bundle: .module
publicAccess: true
output: ${SRCROOT}/Palette/Sources/Palette/Palette.swift
Итоговый внешний вид модуля Palette
Модуль Palette мы с вами настроили. Теперь надо настроить запуск SwiftGen, чтобы палитра генерировалась при старте сборки. Для этого заходим в конфигурацию каждого таргета и создаем новую Build Phase — назовём ее Palette generator. Не забудьте перенести эту Build Phase на максимально высокую позицию.
Теперь прописываем вызов для SwiftGen:
cd ${SRCROOT}/Palette
/usr/bin/xcrun --sdk macosx swift run -c release swiftgen config run --config ./Scripts/swiftgen.yml
Note: /usr/bin/xcrun --sdk macosx — очень важный префикс. Без него при сборке вылетит ошибка: «unable to load standard library for target 'x86_64-apple-macosx10.15».
Пример вызова для SwiftGen
Готово — доступ к цветам можно получить следующим образом:
Palette.myGreen (Color type in SwiftUI) и PaletteCore.myGreen (UIColor/NSColor).
Подводные камни
Перечислю то, с чем мы успели столкнуться.
- Всплывают ошибки архитектуры и портят всю логику разбиения на модули.
- SwiftLint & SwiftGen не уживаются вместе при подгрузке их через SPM. Причина в разных версиях yml.
- В крупных проектах не получится сразу избавиться от Cocoapods. А разбивать уже созданный проект с закреплёнными версиями подов — настоящее испытание, потому что SPM только развивается и не везде поддерживается. Но SPM и Cocoapods более-менее работают параллельно: разве что поды могут кидать ошибку «MergeSwiftModule failed with a nonzero exit code». Это происходит довольно редко, а решается очисткой и пересборкой проекта.
- На данный момент SPM не позволяет прописать пути поиска библиотек. Приходится явно указывать их с завязкой на -L$(BUILD_DIR).
SPM — замена Bundler?
В этом вопросе предлагаю помечтать и подискутировать в комментариях. Тему нужно хорошо изучить, но выглядит она очень интересно. Кстати, уже есть интересная довольно близкая статья о плюсах и минусах SPM.
SPM дает возможность вызывать swift run, если добавить Package.swift в корень вашего проекта. Что это нам дает? К примеру, можно вызвать fastlane или swiftlint. Пример вызова:
swift run swiftlint --autocorrect.
===========
Источник:
habr.com
===========
Похожие новости:
- [Open source, PHP, Программирование, Компиляторы] ВКонтакте снова выкладывает KPHP
- [Программирование, C++] Работа с файлами в C++ с использованием Boost
- [Мессенджеры, Разработка мобильных приложений, API, Софт, Видеоконференцсвязь] Top Chat Software to Integrate on Online Consultation Apps for Better Collaboration & Business Outcome
- [Разработка веб-сайтов, JavaScript, Программирование] 20+ ES6-сниппетов для решения практических задач (перевод)
- [Программирование, Java, SQL, Kotlin] Spring: Ускоряем запись в базу данных с помощью XML
- [Разработка мобильных приложений, Дизайн мобильных приложений, Карьера в IT-индустрии] Как начать работать на React Native, чтобы не было мучительно больно
- [Python, Программирование] Метаклассы в Python (перевод)
- [PHP, Программирование, Laravel] Печатные формы документов для Eloquent в 0 строчек кода
- [JavaScript, Программирование, Я пиарюсь, Lisp] Как я устал от JavaScript и создал свой собственный язык программирования
- [Программирование, Data Mining, ООП, R, Data Engineering] ООП в языке R (часть 2): R6 классы
Теги для поиска: #_programmirovanie (Программирование), #_razrabotka_pod_ios (Разработка под iOS), #_razrabotka_mobilnyh_prilozhenij (Разработка мобильных приложений), #_surf, #_surfstudio, #_swift, #_ios, #_ios_development, #_ios_razrabotka (ios разработка), #_razrabotka_mobilnyh_prilozhenij (разработка мобильных приложений), #_spm, #_modular, #_architecture, #_modularization, #_macos, #_dependencies, #_swiftgen, #_blog_kompanii_surf (
Блог компании Surf
), #_programmirovanie (
Программирование
), #_razrabotka_pod_ios (
Разработка под iOS
), #_razrabotka_mobilnyh_prilozhenij (
Разработка мобильных приложений
)
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:53
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
Привет, Хабр! Меня зовут Эрик Басаргин, я iOS-разработчик в Surf. На одном большом проекте мы столкнулись с низкой скоростью сборки — от трёх минут и более. Обычно в таких случаях студии практикуют модуляризацию проектов, чтобы не работать с огромными монолитами. Мы в Surf решили поэкспериментировать и модуляризовать проект с помощью Swift Package Manager — менеджера зависимостей от Apple. О результатах расскажем в другой статье, а сейчас ответим на главные вопросы: зачем это всё нужно, почему мы выбрали SPM и как делали первые шаги. Почему именно SPM Ответ прост — это нативно и ново. Он не создает overhead в виде xcworkspace, как Cocoapods, к примеру. К тому же SPM — open-source проект, который активно развивается. Apple и сообщество исправляют в нем баги, устраняют уязвимости, обновляют вслед за Swift. Делает ли это сборку проекта быстрее Теоретически сборка ускорится из-за самого факта разделения приложения на модули — framework'и. Это значит, что каждый модуль будет собираться только в том случае, когда в него внесли изменения. Но утверждать точно можно будет только по окончанию эксперимента. Note: Эффективность модуляризации напрямую зависит от правильного разбиения проекта на модули. Как сделать эффективное разбиение Метод разбиения зависит от выбранной архитектуры, типа приложения, его объема и планов на дальнейшее развитие. Поэтому я расскажу о трёх правилах, которых мы стараемся придерживаться при разбиении. Разделять обобщённую функциональность. Каждый модуль отвечает за какую-то категорию, к примеру:
Разделять flow'ы приложения. Рассмотрим древовидную структуру, где MainFlow — главное flow приложения. Представим, что у нас новостное приложение.
Выносить reusable компоненты в отдельные модули:
Когда нужно выносить компонент в отдельный модуль Допустим, у нас есть конкретный flow, и мы хотим добавить в него новые функции. Если компонент будет переиспользоваться или это теоретически возможно в будущем — лучше вынести его в отдельный модуль или аналог CommonUIComponents. В других случаях можно оставить компонент локальным. Такой подход решает проблему с потерей компонентов. Это происходит в больших проектах, и если компонент не задокументировали, то его поддержка и отладка впоследствии станет убыточной. Создаём проект с использованием SPM Рассмотрим создание тривиального тестового проекта. Я использую Multiplatform App project на SwiftUI. Платформа и интерфейс тут не имеют значения. Note: Чтобы быстро создать Multiplatform App, нужен XCode 12.2 beta. Создаём проект и видим следующее: Теперь создадим первый модуль Common:
Note: Достаточно подключать к таргетам только корневые модули. Они не добавлены как подмодули. Подключение завершено. Теперь достаточно настроить модуль с public интерфейсом — и вуаля, первый модуль готов. Как подключить зависимость у локального SPM-пакета Добавим пакет AdditionalInfo — как Common, но без добавления к таргетам. Теперь изменим Package.swift у Common пакета. Добавлять больше ничего не нужно. Можно использовать. Пример, приближенный к реальности Подключим к нашему тестовому проекту SwiftGen и добавим модуль Palette — он будет отвечать за доступ к палитре цветов, утвержденной дизайнером.
xcassets:
inputs: - ${SRCROOT}/Palette/Sources/Palette/Palette.xcassets outputs: - templatePath: ${SRCROOT}/Palette/Templates/palette.stencil params: bundle: .module publicAccess: true output: ${SRCROOT}/Palette/Sources/Palette/Palette.swift Итоговый внешний вид модуля Palette Модуль Palette мы с вами настроили. Теперь надо настроить запуск SwiftGen, чтобы палитра генерировалась при старте сборки. Для этого заходим в конфигурацию каждого таргета и создаем новую Build Phase — назовём ее Palette generator. Не забудьте перенести эту Build Phase на максимально высокую позицию. Теперь прописываем вызов для SwiftGen: cd ${SRCROOT}/Palette
/usr/bin/xcrun --sdk macosx swift run -c release swiftgen config run --config ./Scripts/swiftgen.yml Note: /usr/bin/xcrun --sdk macosx — очень важный префикс. Без него при сборке вылетит ошибка: «unable to load standard library for target 'x86_64-apple-macosx10.15». Пример вызова для SwiftGen Готово — доступ к цветам можно получить следующим образом: Palette.myGreen (Color type in SwiftUI) и PaletteCore.myGreen (UIColor/NSColor). Подводные камни Перечислю то, с чем мы успели столкнуться.
SPM — замена Bundler? В этом вопросе предлагаю помечтать и подискутировать в комментариях. Тему нужно хорошо изучить, но выглядит она очень интересно. Кстати, уже есть интересная довольно близкая статья о плюсах и минусах SPM. SPM дает возможность вызывать swift run, если добавить Package.swift в корень вашего проекта. Что это нам дает? К примеру, можно вызвать fastlane или swiftlint. Пример вызова: swift run swiftlint --autocorrect.
=========== Источник: habr.com =========== Похожие новости:
Блог компании Surf ), #_programmirovanie ( Программирование ), #_razrabotka_pod_ios ( Разработка под iOS ), #_razrabotka_mobilnyh_prilozhenij ( Разработка мобильных приложений ) |
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 17:53
Часовой пояс: UTC + 5