Старший iOS-разработчик red_mad_robot Аня Кочешкова рассказывает, чем отличаются три менеджера зависимостей, в каких случаях и для каких задач подойдёт тот или иной. Материал будет полезен джун-специалистам, которые только начали погружаться в разработку: специально для них подробно объясняем, что такое семантическое версионирование, как устроены модули кода и в чём разница между динамическими и статическими библиотеками.


Что такое менеджер зависимостей и зачем он нужен

В современной разработке зависимость — это написанный кем-то другим код, который используется в вашей программе. Добавление зависимости позволяет воспользоваться уже сделанной кем-то хорошей работой и избежать «изобретения велосипеда», а также лишних действий: проектирования, написания, тестирования, отладки и поддержки определённой логики. Такие «куски логики» называют пакетами, библиотеками или модулями.

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

Эту работу можно проиллюстрировать таким бытовым примером. Чтобы телефон показал погоду на улице, не нужно встраивать в него термометр и вытаскивать за окно. В нём уже зашита специальная технология, которая позволяет ему связаться с «библиотекой», где хранятся данные гидрометцентра, которые, как и погода, всё время обновляются. Иногда с новой версией добавляется какая-нибудь новая функциональность. В примере с термометром библиотека с новой версией умеет ещё и выдавать карту дождей, а до этого могла показывать только температуру.

Менеджеры зависимостей позволяют избежать долгого и муторного решения такого набора «уравнений» — то есть берут на себя часть работы, которую без них разработчик делал бы руками. Это система, которая позволяет управлять зависимостями в вашем проекте. А ещё у сторонних библиотек часто могут выходить новые версии с исправленными ошибками, и благодаря менеджеру зависимостей вам не нужно следить за их выходом — он всё обновит за вас.

Предположим, вы решили подключить в ваш проект Firebase Crashlytics и GoogleUtilities. Обе эти библиотеки ссылаются на библиотеку PromisesObjC. Firebase хочет, чтобы она была любой версии, начинающейся с 2.1, а GoogleUtilities — чтобы она была от 1.2 до 3.0. Такое уравнение, конечно, можно решить вручную, но что делать, когда этих уравнений несколько десятков, а уровней зависимостей — два, три или больше?

Для таких задач в распоряжении iOS-разработчика существуют три основных менеджера зависимостей:

  1. Cocoapods. Пожалуй, самый распространённый сегодня, он требует лишь указывать список зависимостей в определённом формате в текстовом файле.

  2. Carthage. Менее удобен в этом плане: вы также указываете список зависимостей в текстовом файле, но помимо этого нужно ещё немного «самостоятельной работы», о которой расскажем ниже.

  3. SPM, или Swift Package Manager. Официальный менеджер зависимостей от Apple. Появился недавно, и многие команды переходят на него с Cocoapods. Позволяет управлять зависимостями как через графический, так и через текстовый интерфейс прямо из Xcode.

Случается, что нужная библиотека не поддерживает необходимый менеджер зависимостей, потому что разработчики каждой библиотеки сами выбирают, какой менеджер поддержать. Поддержка каждого менеджера — это работа, которая занимает определённое время, поэтому часто выбирают работать только с самым популярным менеджером, Сocoapods. А если в проекте, например, SPM, а библиотека есть только в Cocoapods или просто нужна в одной конкретной версии, приходится добавлять вручную как framework / xcframework, о чём мы также поговорим позже.

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

Фантастическое Semantic Versioning, и где оно обитает

Semantic Versioning (SemVer) — это стандарт, который говорит о том, как работать с версиями пакетов/библиотек/приложений, — как их указывать и накладывать на них ограничения.

Зачем накладывать ограничения? Возьмём такой пример. Разработчик создаёт приложение и использует некоторый набор библиотек для упрощения работы — например, библиотек для работы с сетью Alamofire. Допустим, начиная с определённой версии в библиотеке появляется поддержка какой-то новой функциональности, например Modern Concurrency. Эта функциональность доступна только начиная с определенной версии библиотеки. В таком случае от разработчика потребуется всего лишь поднять версию Alamofire в манифесте, где он описывает используемые библиотеки. Если начиная с какой-то версии библиотеки исправляется некий важный баг, но не сильно меняется функциональность самой библиотеки, то обновление до свежей исправленной версии произойдёт без лишнего вмешательства разработчика, в следующий раз при обновлении текущих библиотек проекта.

Разберём, что такое SemVer, чтобы понимать формат версий, с которыми придётся работать, а также узнаем, как можно указать версию зависимости и наложить на неё ограничения.

Этот набор правил — глобальный стандарт, который используется повсеместно, начиная от пакетов Node.js и заканчивая версиями приложений в App Store.

По этому стандарту номер версии — это строка, которая состоит из чисел, разделённых точкой, и имеет формат A.B.C, где A — мажорная версия, B — минорная версия и C — патч-версия. Версии сравниваются слева направо, например: 1.0.0 < 2.0.0 < 2.1.0 < 2.1.1.

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

Синтаксис SemVer в Cocoapods, Carthage и SPM

Cocoapods

В Cocoapods можно указать одну из стратегий выбора версии:

  1. Если необходима конкретная версия, просто укажите её без дополнительных символов, как есть: '5.0.0'.

  2. Если необходима точная версия, но при этом вы также хотите обновляться, если придут исправления багов в патч-версии, используйте операнд ~>: '~> 5.0.0'.

  3. Можно указывать неравенства: '> 5.0.0', '≥ 5.0.0' или '< 5.0.0', '≤ 5.0.0'.

В этом случае Cocoapods обновит библиотеку до 5.0.1, 5.0.2 и т. д., но не возьмёт обновление 5.1.0, так как в нём уже повышена минорная, а не патч-версия. Если вы также хотите завязаться на минорную версию, достаточно просто указать '~> 5.0'.

Carthage

В Carthage используется тот же формат, что и в Cocoapods, с тем только исключением, что точная версия указывается через равенство: == 5.0.0.

SPM

SPM позволяет указать стратегию выбора версии прямо в интерфейсе, доступны следующие:

Аналогичный синтаксис и у Package.swift файла.

Модули — что к чему: библиотеки, фреймворки, XCFramework и Swift-пакеты

Организация кода в iOS построена на понятии модулей. Модуль — это отдельный кусок кода или функциональности, который можно распространять разными способами и переиспользовать.

Существует четыре вида модулей. Разберёмся, в чём разница:

  1. Библиотека — это исполняемый код, скомпилированный под конкретную архитектуру.

  2. Фреймворк — папка, содержащая в себе библиотеку и вспомогательные файлы.

  3. XCFramework — набор фреймворков под разные архитектуры.

  4. Swift-пакет — фреймворк, который используется для работы со Swift Package Manager.

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

Библиотеки

Библиотека — это исполняемый код, скомпилированный под конкретную архитектуру, например arm64.

Архитектуры могут различаться для разных типов устройств и симуляторов:

  • iOS-устройства — архитектуры armv7, armv7s, arm64,

  • iOS-симулятор — архитектуры i386 и x86_64.

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

Библиотеки бывают статическими и динамическими (об этом позже), и представляют собой один бинарный файл.

Если представить, что библиотека — это настоящая библиотека со стеллажами, в которых хранятся книги, то эти книги — справочники о том, как что-то сделать (методы для выполнения разных задач). По сути, это скомпилированный набор объектных файлов, которые программа может использовать для выполнения любой задачи. То есть все файлы, которые библиотека собирается использовать, были скомпилированы до этапа компоновки.

Фреймворки

Фреймворк (.framework) представляет собой папку с файлами, в которой лежит сама библиотека, хедеры и ресурсы. Эта папка должна иметь определённую структуру, но нет нужды запоминать её, так как фреймворки, как правило, собираются самим Xcode. Поддержка фреймворков добавилась в iOS 8.

Фреймворки служат той же цели, что и библиотеки, но могут включать в себя различные ресурсы — картинки, документацию, строковые файлы.

Они также могут быть статическими или динамическими.

Что использовать: фреймворк или библиотеку

В чём же разница между фреймворком и библиотекой? И когда их использовать? Принципиальное различие — в архитектуре.

XCFrameworks

Для начала стоит рассказать, что такое fat-фреймворки. Это такие фреймворки, которые содержат библиотеки для разных архитектур и платформ, соединённые в одну. С появлением Xcode 11 на замену им пришли XCFrameworks, которые ввели структуру, позволяющую разнести библиотеки для разных платформ и архитектур по папкам.

XCFramework — это, по сути, такой усовершенствованный fat-фреймворк. Он также содержит различные библиотеки для разных архитектур и платформ, но теперь нет необходимости трудиться над тем, чтобы объединить их в одну. В одном XCFramework может лежать несколько разных фреймворков — для iOS, macOS, tvOS, watchOS, а также для разных архитектур — armv7, x86_64 и т. д., каждый в своей папке.

В каком-то смысле фреймворк — тоже разновидность библиотеки, а XCFramework — разновидность фреймворка, но всё же эти понятия принято разделять. Создатель зависимости сам выбирает, в каком виде её распространять. Нам важно понимать различие между этими видами, выбор можно сделать, только создавая свою библиотеку.

Swift Package

Swift-пакет используется для распространения только через SPM. Содержит исходный код, а также Package.swift файл, который описывает его конфигурацию. Доступен начиная со Swift 3.

Static vs Dynamic

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

Первое, что здесь важно понимать: системные iOS- и macOS-библиотеки — динамические. Приложения могут хранить ссылку на библиотеку. Например, системная библиотека Foundation — динамическая. Она лежит на айфоне в единственном экземпляре, а все приложения лишь ссылаются на неё.

Фреймворки, как и библиотеки, бывают динамические и статические и представляют собой обёртку библиотеки. Так, динамические библиотеки вне фреймворков не поддерживаются на iOS, watchOS и tvOS, и здесь важно отметить, что во всех дальнейших сравнениях, говоря «динамическая библиотека», мы будем иметь в виду её использование в рамках динамического фреймворка.

Фреймворки в Cocoapods и Carthage

Зависимости, распространяемые через Cocoapods, называются кокоаподами, или подами. До iOS 8 под представлял собой fat-статическую библиотеку.

Есть специальная инструкция — по ней Cocoapods использует фреймворки вместо статичных библиотек, которые не поддерживались Swift до Xcode 9 и CocoaPods 1.5.0.

Так что раньше эта директива была обязательна, сейчас — опциональна. Если нужно более быстрое время запуска, можно её убрать (см. предыдущий раздел).

target 'TargetName' do
  use_frameworks!

end

Carthage поддерживает только фреймворки.

Три менеджера зависимостей: Cocoapods, Carthage, SPM

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

Cocoapods

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

Как правило, почти все библиотеки поддерживают Cocoapods, хотя с развитием SPM стали появляться и такие, которые поддерживают исключительно SPM (в основном это библиотеки Apple). В таком случае, если ваш менеджер зависимостей Cocoapods, единственный вариант воспользоваться библиотекой — вручную её собрать и прилинковать к проекту.

Чтобы понять, поддерживает ли библиотека Cocoapods, достаточно заглянуть в readme, либо обратить внимание на наличие .podspec-файла в корне репозитория.

Все библиотеки, опубликованные в Cocoapods, можно посмотреть на их официальном сайте.

Cocoapods в вашем проекте

Для понимания работы Cocoapods важны два термина — подфайл и спека. Подфайл описывает все зависимости приложения, а спека — библиотеку (её название и набор её зависимостей) для Cocoapods. Спеки хранятся в общем репозитории спек Cocoapods. Ещё их можно хранить в приватных репозиториях.

Для описания конфигурации зависимостей используется подфайл. Этот файл написан на языке Ruby.

  1. В файле опционально укажите источник спек. Источников может быть несколько, даже приватные.

    source 'https://github.com/CocoaPods/Specs.git' — официальный источник спек, который указан по умолчанию.

    source 'https://github.com/Artsy/Specs.gi — можно указать свои источники спек, в том числе приватные.

  2. Укажите минимальную версию iOS: platform: ios, '9.0'.

  3. Укажите таргет и набор зависимостей для него. Если таргетов несколько, укажите все, так как зависимости для них могут различаться.

  4. Декларируйте зависимость. Первым идёт её официальное название (то самое, которое указано в спеке), затем версия. Используйте в названии правила из блока про SemVer.

    target 'MyApp' do
    pod 'GoogleAnalytics', '~> 3.1'

    ...
    end

Есть и другие способы указать зависимости.

Это может понадобиться в случаях, когда нужно использовать какую-то конкретную версию библиотеки. Например, из определённой ветки репозитория, где она хранится, или даже из определённого коммита. Либо указать путь локально, если есть только её файл.

  1. Можно указать ресурс репозитория, с которого будем загружать библиотеку:
    pod 'CRToast', :git => 'https://github.com/akhatmullin/CRToast.git'

  2. Можно указать нужную ветку в репозитории:
    pod 'CRToast', :git => 'https://github.com/akhatmullin/CRToast.git', :branch => 'dev'

  3. Или тег:
    pod 'CRToast', :git => 'https://github.com/akhatmullin/CRToast.git', :tag => '0.7.0'

  4. Или коммит:
    pod 'CRToast', :git => 'https://github.com/akhatmullin/CRToast.git', :commit => '082f8310af'

  5. Можно указать на локальный под (путь должен вести на папку, где лежит .podspec):
    pod 'AFNetworking', :path => '~/Documents/AFNetworking'

Рядом с подфайлом обычно лежит Podfile.lock. Этот файл описывает конечные версии зависимостей после их установки. Он обычно так же заливается в GitHub, как и сам Podfile, чтобы избежать ошибок и рассинхрона при установке зависимостей у разных разработчиков. Ещё он ускоряет выполнение pod install.

Чтобы приступить к работе, запустите pod install. Можно выполнить pod update, если вы хотите обновить все библиотеки в проекте, иначе же будут использоваться фиксированные версии зависимостей из Podfile.lock.

Как Cocoapods работает под капотом

Pod install генерирует workspace — файл .xcworkspace, состоящий из двух проектов — проекта с вашим приложением и отдельного проекта, в котором хранятся зависимости (поды).

Чтобы всё работало корректно, всегда открывайте workspace-файл. Если внимательнее посмотреть, что происходит, можно увидеть, что продукт этого проекта линкуется в основной проект:

Также Cocoapods добавляет пару скриптов в Build Phases:

Поддержка Cocoapods в вашей библиотеке

Чтобы библиотека была доступна через Cocoapods, нужно сначала описать библиотеку или под при помощи спеки, собрать её и отправить спеку в приватный либо глобальный репозиторий Cocoapods.

Разберёмся, как это сделать.

  1. Создайте спеку и файл лицензии.

Лицензию нужно добавлять для любой библиотеки, и чаще всего используется лицензия MIT — самая популярная лицензия для программ с открытым исходным кодом. Её текст можно найти в интернете. Здесь нужно описать вашу библиотеку. Файл принято форматировать так, чтобы вторая часть строки была выровнена.

    Pod::Spec.new do |spec|

    # Название библиотеки. Именно его будут указывать разработчики в своих Podfil
    spec.name                    = 'NAME' 
    # Версия
    spec.version                 = '1.2.1' 
    # Описание
    spec.summary                 = 'Describe your framework.'
    # Здесь можно указать путь на вебсайт или просто GitHub-репозиторий
    spec.homepage                = 'https://xxx' 
    # Путь к лицензии. Да, её тоже нужно создать
    spec.license                 = { type: 'MIT', file: 'LICENSE' } 
    # Автор библиотеки
    spec.author                  = { "Your Name" => 'your-email@example.com' } 
    # Путь к исходным файлам (чаще всего это ваш репозитоий)
    spec.source                  = { :git => 'https://github.com/path-to-repo.git', :branch => "master"} 

    # Минимальная поддерживаемая версия iOS и Swift
    spec.ios.deployment_target   = '13.0' 
    spec.swift_version           = '5.0'

    # Если библиотека использует локальный фреймворк, его нужно указать 
    spec.vendored_frameworks     = 'Path/To/Files/NameOfVendor.xcframework'
    # Указываем вайлдкард-пути к файлам, которые необходимо включить
    spec.source_files            = 'Path/To/Files/**/*.{swift}' 
    # Путь к ресурсам
    spec.resources               = 'Path/To/Files/Resources/**/*.{strings}' 
    # Укажите здесь все ассеты, которые вам нужны, включая .xib-файлы
    spec.resource_bundles        = {'BundleName' => ['Path/To/Files/Resources/*.{xcassets,plist,xib}' 

    # Указываем зависимости
    spec.dependency           "Alamofire", "~> 5.0.0" 

    # И системные зависимости
    spec.frameworks           = 'Foundation' 
    spec.ios.frameworks       = 'UIKit'
    end
  1. Проверьте, что всё корректно.

Следующая команда валидирует спеку — проверяет, что она не имеет ошибок, а проект билдится — pod lib lint.

  1. Отправьте спеку в репозиторий спек.

Как правило, это делается уже на финальном этапе разработки — после того, как прошла проверка и вы готовы публиковать работу, — pod trunk push NAME.podspec.

Carthage

Carthagе — это децентрализованный менеджер зависимостей, которые не нужно «поддерживать» разработчику. Carthage сам выкачивает библиотеку из репозитория, компилирует её и предоставляет в готовом виде. Поэтому, если хотите использовать Carthage, не нужно искать в репозитории с библиотекой определённые инструкции или слова о том, что он поддерживается, — просто попробуйте.

Нет у Carthage и централизованного источника, как у Cocoapods. Этим источником, по сути, является сам GitHub.

Конечно, этого не всегда бывает достаточно. Некоторые библиотеки Carthage просто не может «переварить» — что-то не собирается, а что-то отваливается. Задача разработчика — проверить, всё ли работает корректно.

Наконец, Carthage может автоматически подхватить уже собранный фреймворк, если он прикреплён к соответствующему релизу на GitHub.

Carthage в вашем проекте

Зависимости в Carthage описываются в текстовом файле Cartfile. Он очень похож на Podfile, только гораздо проще по структуре. Мы указываем источник, имя зависимости и версию.

  1. Источник зависимости. Как правило, GitHub, либо git для универсальных репозиториев Git, размещённых в другом месте. Ключевое слово git сопровождается путём к репозиторию, будь то удалённый URL-адрес, используя git://, http://, или ssh://, или локальный — путём к репозиторию git на компьютере разработчика.

  2. Название. Через слеш указывается владелец и название репозитория, которые можно подсмотреть в ссылке на репозиторий.

  3. Версия. Особенности указания версий мы уже рассмотрели в блоке про семантическое версионирование, но можно также указывать и ветки.

Примеры:

  1. Версия — github "Alamofire/Alamofire" == 2.0

  2. Ветка — github "username/project" "branch"

  3. Локальный проект — git "file:///directory/to/project" "branch"

  4. Бинарник (путь указывается в json) — binary "https://my.domain.com/release/MyFramework.json" ~> 2.3

После описания зависимостей потребуется ещё несколько дополнительных шагов.

  1. Вызовите carthage update. Carthage склонирует репозитории с указанных в Cartfile путей и затем сбилдит для каждой зависимости фреймворк. После окончания работы вы увидите несколько файлов и каталогов:

  1. Полученные фреймворки затем вручную перетащите из папки Build в проект в секцию Frameworks, Libraries и Embedded Content.

  2. Carthage требует добавления нового скрипта в Build Phases в качестве workaround бага Apple. Это позволяет dSYM-файлам корректно подтягиваться при архивации.

  1. Перейдите в Build Phases и добавьте новый Run Script. Добавьте следующую команду: /usr/local/bin/carthage copy-frameworks.

  2. Нажмите на плюс под Input Files и добавьте запись для каждой платформы: $(SRCROOT)/Carthage/Build/iOS/Alamofire.framework.

Как Carthage работает под капотом

Как и в случае с Cocoapods, создаётся лок-файл Cartfile.resolved, который фиксирует конечные версии зависимостей.

Каталог Carthage содержит два подкаталога:

  1. Build. Содержит собранный фреймворк для каждой зависимости.

  2. Checkouts. Содержит клонированные исходники, которые Carthage потом компилирует. Это своего рода кэш, который ускоряет работу.

Поддержка Carthage в вашей библиотеке

Для поддержки Carthage достаточно сделать релиз на GitHub. Но никогда не помешает проверить, всё ли билдится корректно в вашем случае и, если есть какие-то проблемы, решить их.

Для этого, выполните carthage build --no-skip-current. Carthage попытается сбилдить ваш проект и создаст в нём папку Carthage. Также стоит обратить внимание, что он видит только те схемы, которые помечены в проекте как Shared.

SPM

Или Swift Package Manager — официальный менеджер зависимостей от Apple.

Работа с ним происходит прямо в Xcode, а .Xcodeproj файлы больше не нужны (хотя при желании есть возможность сгенерировать файл проекта из SPM-пакета). Если заглянуть в репозиторий библиотеки, то первое, что будет говорить о поддержке SPM, — это наличие Package.swift-файла.

SPM в вашем проекте

Добавление зависимостей в проект происходит в секции Package Dependencies.

  1. Для начала выберите File > Swift Packages > Add package dependecy.

  1. Найдите модуль по поиску, либо введите URL репозитория.

  2. Укажите версию:

Как и во всех предыдущих случаях, SPM создает Package.resolved, назначение которого то же, что и у других менеджеров зависимостей.

В меню File > Swift Packages доступно несколько действий:

  • Reset Package Caches удаляет кэши из Derived Data.

  • Resolve Package Versions резолвит версии зависимостей. Создаёт Package.resolved, если его не существует, но не меняет его, если он уже есть. Может потребоваться, если Package.resolved был, например, обновлён извне.

  • Update to Latest Packages Versions обновляет версии пакетов, может модифицировать Package.resolved.

Поддержка SPM в вашей библиотеке

Если вы разрабатываете библиотеку и хотите распространять её через SPM, нужно будет создать Package.swift файл. Таким образом может быть сконфигурирован и обычный проект, что позволяет отойти от использования project-файлов. Конфигурация через SPM выглядит чище и более проста для понимания и редактирования, чем стандартные project-файлы, которые зачастую нечитаемы.

Если вы пишете библиотеку и хотите распространять её через SPM, обязательно убедитесь, что потенциальные потребители библиотеки не используют Cocoapods. Одновременная поддержка Cocoapods и SPM — часто непростая задача.

import PackageDescription
 
let package = Package(
    // Название нашего пакета
    name: "Resources",
    // Платформы, которые поддерживаются нашим пакетом
    platforms: [
        .iOS(.v11),
    ],
    // То, что будут использовать сторонние приложения
    products: [
        .library(
            name: "Resources",
            // Динамический или статический продукт
            // по дефолту значение nil - SPM сам будет понимать, что лучше подходит
            // преференция, скорее всего, будет отдаваться .static
            type: .dynamic,
            targets: ["Resources"]),
    ],
        // Зависимости, необходимые для работы нашего пакета,
  	// здесь они просто загружаются, добавляются они в targets
    dependencies: [
        // Название пакета, путь к нему и источник (ветка, версия, локальный путь и т. д.)
        .package(
            name: "R.swift.Library", 
            url: "https://github.com/mac-cain13/R.swift.Library", 
            branch: "master"),
        .package(
            name: "SVGKit",
            url: "https://github.com/SVGKit/SVGKit.git",
            .upToNextMajor(from: "3.0.0")),
        .package(
            name: "PhoneNumberKit",
            url: "https://github.com/marmelroy/PhoneNumberKit",
            from: "3.3.4"),
        // Пример подключения локального пакета
        .package(path: "../Core")
    ],
    targets: [
        // Это то, из чего мы будем складывать наш продукт
        .target(
            name: "Resources",
            dependencies: [
                // Здесь мы указываем зависимости, которые мы хотим использовать в таргете
                .product(name: "RswiftDynamic", package: "R.swift.Library")
            ],
            resources: [
                // Все ресурсы, которые мы хотим использовать, нужно явно указать
                // Путь к ним относительный от Sources/имя_пакета/то_что_мы_указали
                .process("Resources")
            ])
    ]
)

И как выбрать подходящий менеджер

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

SPM чаще всего не будут поддерживать старые библиотеки, в то время как Cocoapods вполне могут не поддерживать новые (хотя это и большая редкость, чем первое). Carthage в этом плане может показаться универсальнее. Хоть он более громоздкий и сложный во взаимодействии, но он единственный разделяет шаги управления зависимостями и добавления их в проект. Это даёт контроль над ситуацией и ускоряет разработку — не будет лишних пересборок или заморозки интерфейса, потому что нужно зарезолвить зависимости.


Над материалом работали:

  • текст — Аня Кочешкова, Ника Черникова,

  • редактура — Виталик Балашов,

  • иллюстрации — Юля Ефимова.

Делимся железной экспертизой от практик в нашем телеграм-канале red_mad_dev. А полезные видео складываем на одноимённом YouTube-канале. Присоединяйся!

Комментарии (11)


  1. bshkrva
    00.00.0000 00:00
    +1

    Спасибо за материал!


  1. aokruan
    00.00.0000 00:00
    +1

    Полезная статья. Понравилось про версионирование.


  1. house2008
    00.00.0000 00:00
    -1

    По тексту это просто перевод Readme для каждого менеджер зависимостей. А где важные детали ? например:

    1. Cocoapods: какие файлы нужно комитать в гит ? Нужно ли комитать Podfile.lock файл ? Нужно ли комитать Pods директорию, какие плюсы/минусы для Pods директории это имеет.

    2. Carthage: нужно ли комитать Cartfile.resolved ? Нужно ли комитать Checkout & Build директории, если да то какие плюсы минусы.

    3. SPM: где хранятся исходники скаченных библиотек и как это связано с очищением DerivedData

    4. Cocoapods если добавить 50 зависимостей в виде динамических библиотек, что произойдет с временем запуска приложения ?

    5. При работе с каким менеджером у Xcode наилучший перформанс


    1. redmadrobot Автор
      00.00.0000 00:00

      Привет! Спасибо за комментарий.

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

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


      1. house2008
        00.00.0000 00:00

        Привет, но у вас написано:

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

        Я джун, добавил зависимость через Cocoapods как вы написали, git status -sb мне показал ./Pods/ директорию как новую, мне ее нужно комитать в проект или добавить в игнор ? Спасибо.


        1. redmadrobot Автор
          00.00.0000 00:00

          Нет, resolved и lock файлы, а также Pods и Carthage директории коммитить не нужно. 

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


          1. house2008
            00.00.0000 00:00
            -1

            Carthage директории коммитить не нужно

            Спорное утверждение. В проекте 20 библиотек, при запуске тестов на CI их сборка занимает 10 минут (так как вы писали Carthage собирает все схемы), а сами тесты проходят за 3 минуты, если их закоммитить в проект (бинарники папку Carthage/Build) то на каждый прогон тестов их собирать не надо, то есть +10 минут к сборке приложения (да, не без минусов).

            resolved коммитить не нужно.

            Откуда тогда Carthage узнает какая у вас версия зависимости если она в Cartfile не указана явно, вы писали пример "MyFramework.json" ~> 2.3, как Carthage без resolved поймет нужна 2.3.0 или 2.3.1, или 2.3.9 ?

            lock коммитить не нужно.

            Аналогично с resolved как Cocoapods поймет какие конкретные версии нужно скачать чтобы ваш коллега на своей машине имел полную копию зависимостей как у вас.

            Также из личного опыта почти все современные Carthage зависимости не соберутся без флага --use-xcframeworks. Чтобы по-настоящему собирать из кэша нужно использовать флаг --cache-builds. Также чтобы максимально ограничить сборку под iOS следует передавать флаг --platform iOS, но на моей практике правда от него мало пользы.


            1. laqez
              00.00.0000 00:00
              +2

              Чел, все правильно пишешь.

              Но подумай в следующий раз про tone of voice. Можно же по-другому те же вещи написать, сделать конструктивное дополнение к статье и оставить полезный для сообщества комментарий.

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


              1. house2008
                00.00.0000 00:00

                Но подумай в следующий раз про tone of voice.

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

                А то получается очереднярский псевдосеньор залетел в комменты

                жирный лайк


  1. tikhonov666
    00.00.0000 00:00

    Спасибо за статью, надеюсь рано или поздно все перейдут на SPM)


  1. Blyabtroi
    00.00.0000 00:00

    Полезно для систематизации информации!