Swift Package Manager

Вместе с релизом в open source языка Swift 3 декабря 2015 года Apple представила децентрализованный менеджер зависимостей Swift Package Manager.
К публичной версии приложили руку небезызвестные Max Howell, создатель Homebrew, и Matt Thompson, написавший AFNetworking. SwiftPM призван автоматизировать процесс установки зависимостей, а также дальнейшее тестирование и сборку проекта на языке Swift на всех доступных операционных системах, однако пока его поддерживают только macOS и Linux. Если интересно, идите под кат.
Минимальные требования – Swift 3.0. Чтобы открыть файл проекта потребуется Xcode 8.0 или выше. SwiftPM позволяет работать с проектами без xcodeproj-файла, поэтому Xcode на OS X не обязателен, а на Linux его и так нет.
Стоит развеять сомнения – проект еще в активной разработке. Использование UIKit, AppKit и других фреймворков iOS и OS X SDK как зависимостей недоступно, так как SwiftPM подключает зависимости в виде исходного кода, который потом собирает. Таким образом, использование SwiftPM на iOS, watchOS и tvOS возможно, но только с использованием Foundation и зависимостей сторонних библиотек из открытого доступа. Один единственный import UIKit делает вашу библиотеку непригодной для распространения через SwiftPM.
Все примеры в статье написаны с использованием версии 4.0.0-dev, свою версию можете проверить с помощью команды в терминале
swift package —version
Идеология Swift Package Manager
Для работы над проектом больше не нужен файл *.xcodproj — теперь его можно использовать как вспомогательный инструмент. Какие файлы участвуют в сборке модуля, зависит от их расположения на диске — для SwiftPM важны имена директорий и их иерархия внутри проекта. Первоначальная структура директории проекта выглядит следующим образом:
- Sources – исходные файлы для сборки пакета, разбитые внутри по директориям продуктов – для каждого продукта отдельная папка.
- Tests – тесты для разрабатываемого продукта, разбивка на папки аналогично папке Sources.
- Package.swift – файл с описанием пакета.
- README.md – файл документации к пакету.
Основные компоненты
Теперь давайте разберемся с основными компонентами в SwiftPM:

- Модуль (Module) – набор *.swift–файлов, выполняющий определенную задачу. Один модуль может использовать функционал другого модуля, который он подключает как зависимость. Проект может быть собран на основании единственного модуля. Разделение исходного кода на модули позволяет выделить в отдельный модуль функцию, которую можно будет использовать повторно при сборке другого проекта. Например, модуль сетевых запросов или модуль работы с базой данных. Модуль использует порог инкапсуляции уровня internal и представляет собой библиотеку (library), которая может быть подключена к проекту. Модуль может быть подключен как из того же самого пакета (представлен в виде другого таргета), так и из другого пакета (представлен в виде другого продукта).
- Продукт (Product) – результат сборки таргета (target) проекта. Это может быть библиотека (library) или исполняемый файл (executable). Продукт включает себя исходный код, который относится непосредственно к этому продукту, а также исходный код модулей, от которых он зависит.
- Пакет (Package) – набор *.swift–файлов и manifest-файла Package.swift, который определяет имя пакета и набор исходных файлов. Пакет содержит один или несколько модулей.
- Зависимость (Dependency) – модуль, необходимый для исходного кода в пакете. У зависимости должен быть путь (относительный локальный или удаленный на git-репозиторий), версия, перечень зависимостей. SwiftPM должен иметь доступ к исходному коду зависимости для их компиляции и подключения к основному модулю. В качестве зависимости таргета может выступать таргет из того же пакета или из пакета-зависимости.
Получаем, что зависимости выстраиваются в граф – у каждой зависимости могут быть свои собственные и так далее. Разрешение графа зависимостей – основная задача менеджера зависимостей.
Замечу, что все исходные файлы должны быть написаны на языке Swift, возможности использовать язык Objective-C – нет.
Каждый пакет должен быть самодостаточным и изолированным. Его отладка производится не посредством запуска (run), а с помощью логических тестов (test).
Далее рассмотрим простой пример с подключением к проекту зависимости Alamofire.
Разработка тестового проекта
Перейдем через терминал в папку, где будет лежать наш проект, создадим для него директорию и перейдем в нее.
mkdir IPInfoExample cd IPInfoExample/
Далее инициализируем пакет с помощью команды
swift package init
В результате создается следующая иерархия исходных файлов
├── Package.swift ├── README.md ├── Sources │ └── IPInfoExample │ └── main.swift └── Tests └── IPInfoExampleTests ├ LinuxMain.swift └── IPInfoExampleTests └── IPInfoExampleTests.swift
В условиях отсутствия индекса файла проекта *.xcodeproj менеджеру зависимостей нужно знать, какие исходные файлы должны участвовать в процессе сборки и в какие таргеты их включать. Поэтому SwiftPM определяет строгую иерархию папок и перечень файлов:
- Package-файл;
- README-файл;
- Папка Sources с исходными файлами – отдельная папка для каждого таргета;
- Папка Tests – отдельная папка для каждого тестового таргета.
swift build swift test
для сборки пакета или для запуска теста Hello, world!
Добавление исходных файлов
Создадим файл Application.swift и положим его в папку IPInfoExample.
public struct Application <>
Выполняем swift build и видим, что в модуле уже компилируется 2 файла.
Compile Swift Module 'IPInfoExample' (2 sources)
Создадим директорию Model в папке IPInfoExample, создадим файл IPInfo.swift, а файл IPInfoExample.swift удалим за ненадобностью.
//Используем протокол Codable для маппинга JSON в объект public struct IPInfo: Codable
После этого выполним команду swift build для проверки.
Добавление зависимостей
Откроем файл Package.swift, содержание полно описывает ваш пакет: имя пакета, зависимости, таргет. Добавим зависимость Alamofire.
// swift-tools-version:4.0 import PackageDescription // Модуль, в котором находится описание пакета let package = Package( name: "IPInfoExample", // Имя нашего пакета products: [ .library( name: "IPInfoExample", targets: ["IPInfoExample"]), ], dependencies: [ // подключаем зависимость-пакет Alamofire, указываем ссылку на GitHub .package(url: "https://github.com/Alamofire/Alamofire.git", from: "4.0.0") ], targets: [ .target( name: "IPInfoExample", // указываем целевой продукт – библиотеку, которая зависима // от библиотеки Alamofire dependencies: ["Alamofire"]), .testTarget( name: "IPInfoExampleTests", dependencies: ["IPInfoExample"]), ] )
Далее снова swift build, и наши зависимости скачиваются, создается файл Package.resolved c описанием установленной зависимости (аналогично Podfile.lock).
В случае если в вашем пакете только один продукт, можно использовать одинаковые имена для имени пакета, продукта и таргета. У нас это IPInfoExample. Таким образом, описание пакета можно сократить, опустив параметр products. Если заглянуть в описание пакета Alamofire, увидим, что там не описаны таргеты. По умолчанию создаются один таргет с именем пакета и файлами исходного кода из папки Sources и один таргет с файлом-описанием пакета (PackageDescription). Тестовый таргет при использовании SwiftPM не задействуется, поэтому папка с тестами исключается.
import PackageDescription let package = Package(name: "Alamofire", dependencies : [], exclude: [“Tests"])
Чтобы удостовериться в правильности создания модулей, таргетов, продукта, можем выполнить команду
swift package describe
В результате для Alamofire получим следующий лог:
Name: Alamofire Path: /Users/ivanvavilov/Documents/Xcode/Alamofire Modules: Name: Alamofire C99name: Alamofire Type: library Module type: SwiftTarget Path: /Users/ivanvavilov/Documents/Xcode/Alamofire/Source Sources: AFError.swift, Alamofire.swift, DispatchQueue+Alamofire.swift, MultipartFormData.swift, NetworkReachabilityManager.swift, Notifications.swift, ParameterEncoding.swift, Request.swift, Response.swift, ResponseSerialization.swift, Result.swift, ServerTrustPolicy.swift, SessionDelegate.swift, SessionManager.swift, TaskDelegate.swift, Timeline.swift, Validation.swift
Если у пакета несколько продуктов, то в качестве зависимости мы указываем пакет зависимости, а уже в зависимости таргета указываем зависимость от модуля пакета. Например, так подключен SourceKitten в нашей библиотеке Synopsis.
import PackageDescription let package = Package( name: "Synopsis", products: [ Product.library( name: "Synopsis", targets: ["Synopsis"] ), ], dependencies: [ Package.Dependency.package( // зависимость от пакета SourceKitten url: "https://github.com/jpsim/SourceKitten", from: "0.18.0" ), ], targets: [ Target.target( name: "Synopsis", // зависимость от библиотеки SourceKittenFramework dependencies: ["SourceKittenFramework"] ), Target.testTarget( name: "SynopsisTests", dependencies: ["Synopsis"] ), ] )
Так выглядит описание пакета SourceKitten. В пакете описаны 2 продукта
.executable(name: "sourcekitten", targets: ["sourcekitten"]), .library(name: "SourceKittenFramework", targets: ["SourceKittenFramework"])
Synopsis использует продукт-библиотеку SourceKittenFramework.
Создание файла проекта
Мы можем создать файл проекта для своего удобства, выполнив команду
swift package generate-xcodeproj
и в результате получим в корневой папке проекта файл IPInfoExample.xcodeproj.
Открываем его, видим все исходники в папке Sources, в том числе с подпапкой Model, и исходники зависимостей в папке Dependencies.
Важно отметить, что данный шаг является опциональным при разработке продукта и не влияет на механизм работы SwiftPM. Заметьте, что все исходные файлы располагаются так же, как и на диске.

Проверка подключенной зависимости
Проверим, корректно ли подключилась зависимость. В примере делаем асинхронный запрос к сервису ipinfo для получения данных о текущем ip-адресе. JSON ответа декодируем в модельный объект – структуру IPInfo. Для простоты примера не будем обрабатывать ошибку маппинга JSON или ошибку сервера.
// импортируем библиотеку так же, как при использовании cocoapods или carthage import Alamofire import Foundation public typealias IPInfoCompletion = (IPInfo?) -> Void public struct Application < public static func obtainIPInfo(completion: @escaping IPInfoCompletion) < Alamofire .request("https://ipinfo.io/json") .responseData < result in var info: IPInfo? if let data = result.data < // Маппинг JSON в модельный объект info = try? JSONDecoder().decode(IPInfo.self, from: data) >completion(info) > > >
Далее можем воспользоваться командой build в Xcode, а можем выполнить команду swift build в терминале.
Проект с исполняемым файлом
Выше описан пример для инициализации проекта библиотеки. SwiftPM позволяет работать с проектом исполняемого файла. Для этого при инициализации используем команду
swift package init —type executable.
Привести текущий проект к такому виду также можно, создав файл main.swift в директории Sources/IPInfoExample. При запуске исполняемого файла main.swift является точкой входа.
Напишем в него одну строчку
print("Hello, world!”)
А затем выполним команду swift run, в консоль выведется заветное предложение.
Синтаксис описания пакета
Описание пакета в общем виде выглядит следующим образом:
Package( name: String, pkgConfig: String? = nil, providers: [SystemPackageProvider]? = nil, products: [Product] = [], dependencies: [Dependency] = [], targets: [Target] = [], swiftLanguageVersions: [Int]? = nil )
- name – имя пакета. Единственный обязательный аргумент для пакета.
- pkgConfig – используется для пакетов модулей, установленных в системе (System Module Packages), определяет имя pkg-config-файла.
- providers – используется для пакетов системных модулей, описывает подсказки для установки недостающих зависимостей через сторонние менеджеры зависимостей – brew, apt и т.д.
import PackageDescription let package = Package( name: "CGtk3", pkgConfig: "gtk+-3.0", providers: [ .brew(["gtk+3"]), .apt(["gtk3"]) ] )
- products – описание результата сборки таргета проекта – исполняемый файл или библиотека (статическая или динамическая).
let package = Package( name: "Paper", products: [ .executable(name: "tool", targets: ["tool"]), .library(name: "Paper", targets: ["Paper"]), .library(name: "PaperStatic", type: .static, targets: ["Paper"]), .library(name: "PaperDynamic", type: .dynamic, targets: ["Paper"]) ], targets: [ .target(name: "tool") .target(name: "Paper") ] )
Выше в пакете описано 4 продукта: исполняемый файл из таргета tool, библиотека Paper (SwiftPM выберет тип автоматически), статическая библиотека PaperStatic, динамическая PaperDynamic из одного таргета Paper.
-
Dependencies – описание зависимостей. Необходимо указать путь (локальный или удаленный) и версию.
- targets – описание таргетов. В примере объявляем 2 таргета, второй – для тестов первого, в зависимостях указываем тестируемый.
let package = Package( name: "FooBar", targets: [ .target(name: "Foo", dependencies: []), .testTarget(name: "Bar", dependencies: ["Foo"]) ] )
- swiftLanguageVersions – описание поддерживаемой версии языка. Если установлена версия [3], компиляторы swift 3 и 4 выберут версию 3, если версия [3, 4] компилятор swift 3 выберет третью версию, компилятор swift 4 — четвертую.
Индекс команд
swift package init //инициализация проекта библиотеки swift package init --type executable //инициализация проекта исполняемого файла swift package --version //текущая версия SwiftPM swift package update //обновить зависимости swift package show-dependencies //вывод графа зависимостей swift package describe // вывод описания пакета
Ресурсы
- Пример Тестового проекта
- Swift.org – Package Manager
- Swift Package Manager – Usage
Swift package manager как пользоваться
Разработка под iOS, Xcode, Swift
Рекомендация: подборка платных и бесплатных курсов разработки под ios — https://katalog-kursov.ru/

В этой статье я расскажу о проблемах с которыми я столкнулся при подключении тяжелых зависимостей к iOS проекту с помощью Swift Package Manager и о способе их решения.
Тяжелые зависимости
Для начала давайте определим понятие тяжелой зависимости. Под тяжелой зависимостью я понимаю такую зависимость в которой есть большое кол-во исходного кода. Самый распространённый пример такой зависимости — Firebase. Это набор сервисов от Google которые используются при разработке веб и мобильных приложений. Самые часто используемые сервисы это Firebase Crashlytics — для сбора крэшей и их анализа и Firebase Analytics — для продуктовой аналитики.

Firebase содержит в основном Objective-C и С++ код. Но имеется также и Swift и Objective-C++ код.
Проанализировав исходный код с помощью утилиты cloc , можно обнаружить что там 4 803 файла, 150 000 строчек Objective-C кода и 93 000 строчек С++ кода.
Ещё один пример тяжелой зависимости — AWS Swift SDK Это зависимость для работы с сервисами AWS с помощью Swift. Содержит Swift код, около 2 700 файлов и 3 миллиона строчек Swift кода.
Какие проблемы возникают при подключении тяжелых зависимостей через SPM
Если подключать тяжелые зависимости с помощью Swift Package Manager к Xcode проекту, то возникнут следующие проблемы:
1. Время холодной сборки проекта сильно увеличится
Если подключать тяжелую зависимость стандартным способом, через Swift Package Manager, то к проекту подключаются исходники выбранных библиотек. Из-за этого время сборки приложения сильно увеличится так как необходимо будет компилировать эти файлы.
Пример 1 — Очень простое приложение
Возьмем для примера простейшее приложение без тяжелых зависимостей.

После вызова Clean Build и Build With Timing Summary получаем время холодной сборки 0.4 сек.
Далее подключим тяжелую зависимость, например AWS SDK for iOS. Так как AWS SDK состоит из множества библиотек, я для примера подключил всего одну из них — AWS DynamoDB — NoSQL базу данных.

После вызова Clean Build и Build With Timing Summary получаем время холодной сборки 19 сек. Это произошло из-за того что в процессе сборки компилируются исходные файлы этой библиотеки.
Пример 2 — Много-модульное приложение среднего размера
Приложение среднего размера с несколькими десятками модулей без тяжелых зависимостей собирается за 50 секунд.

Если к этому приложению подключить Firebase Crashlytics, Analytics и Messaging, то оно станет собираться почти на 10 секунд дольше.

Итог
При подключении тяжелой зависимости через SPM холодное время сборки приложения увеличивается. Это приводит к тому что на CI приложение будет дольше собираться перед прохождением тестов. Разработчики будут дольше ждать проверок на CI.
2. Время индексации сильно увеличится
Из-за большого количества исходных файлов в тяжелых зависимостях, написанных на разных языках, время на индексацию проекта в Xcode сильно увеличится.
Чтобы понять какие файлы индексирует Xcode надо получить и изучить логи индексации. Для этого достаточно запустить Xcode следующим образом с помощью Terminal:
SOURCEKIT_LOGGING=3 /Applications/Xcode.app/Contents/MacOS/Xcode &> ~/Desktop/indexing.log
Либо вызвать команду:
defaults write com.apple.dt.Xcode IDEIndexShowLog -bool YES

Эта команда позволяет увидеть логи индексации прямо в Xcode, в панели Report Navigator.
Изучив логи можно увидеть что Xcode индексирует абсолютно все исходники которые есть в подключаемой зависимости. Если она разделена на сотни отдельных библиотек, а подключаем к проекту только одну, то Xcode все-равно будет индексировать исходники всех сотен библиотек.
Пример 1 — Очень простое приложение
Без тяжелых зависимостей индексация всего проекта занимает 12 сек. С подключенной тяжелой зависимостью AWS SDK for iOS индексация всего проекта занимает 7 мин 40 сек.
Пример 2 — Много-модульное приложение среднего размера
Приложение среднего размера с несколькими десятками модулей без тяжелых зависимостей индексируется 2 мин 15 секунд.
Если к этому же приложению подключить Firebase Crashlytics, Analytics и Messaging, то оно станет индексироваться 4 мин 20 сек.
Итог
Так как тяжелые зависимости подключаются как исходники, то Xcode их полностью индексирует. Если на CI это не так важно т.к. индексация происходит в процессе сборки, то на компьютерах разработчиков это серьезная проблема. Нормально писать код можно только после окончания индексации.
3. Баги в Xcode при использовании SPM
В Xcode есть серьезный баг, если к проекту подключены Swift пакеты. Если переключаться между ветками, что разработчики делают часто, то он начнет делать Resolving Package Graph, индексацию и Preparing Editor Functionality. И это несмотря на то что Package.resolved файл не изменился. А теперь самое главное: если к проекту подключены тяжелые зависимости, то сразу после переключения на другую ветку, Xcode начинает нагружать все ядра процессора и несколько десятков секунд, а то и минут невозможно ничего делать с проектом. На эту проблему многие жаловались в Twitter (раз, два, три, четыре) и на форуме Apple (раз, два).
Пример:
Переходим с одной ветки на другую, в которой добавились 2 новых Swift файла, а 8 других изменились.
Смотрим что происходит с Xcode:

Сначала Xcode делает Resolving Package Graph, потом индексацию и в конце Preparing Editor Functionality. В это время сначала нагружается одно ядро процессора на 100%, а потом все 8 на M1. А теперь о причинах. В папке DerivedData есть папка SymbolCache в которой расположен файл project.plist. В этом файле хранится информация обо всех символах в проекте. Этот файл для простого iOS проекта с подключенной тяжелой библиотекой у меня весит 141 Мб. Это чрезвычайно много. При переключении ветки, Xcode, по непонятной причине, начинает удалять из этого файла элементы что отнимает очень много времени. Во время удаления элементов из этого файла, Xcode полностью нагружает главный поток что приводит к его полному зависанию. Появляется так называемый Spinning Wheel. Ниже скриншот из Xcode Instruments → Time Profiler, где видно все проблемы о которых я сказал в этом абзаце.

Но, самое неприятное это то что после этого Xcode нагружает все 8 ядер на M1 маке что приводит к зависанию не только Xcode но и всего компьютера.
Из профайлера сложно понять что конкретно делает Xcode. Но из названий можно понять что он продолжает работать с SymbolCache который чрезвычайно тяжелый.
Итог
Xcode содержит серьёзные баги которые мешают пользоваться Xcode в случае проекта с подключенными тяжелыми библиотеками.
4. Отображение списка зависимостей
Тяжелые зависимости, как правило, разделены на большое кол-во библиотек. Кроме того, при попытке подключить одну библиотеку, например Analytics из Firebase, подключается 12 других. Или, например, если подключить AWS DynamoDB из AWS, то подключается 6 разных библиотек.


Здесь я вижу небольшую проблему в том что дополнительных зависимостей много и они засоряют список зависимостей отображаемых в Xcode.
Решение всех проблем
Чтобы решить все проблемы возникающие при подключении тяжелых зависимостей через SPM достаточно просто отказаться от их подключения в виде исходников. Их надо подключать в виде скомпилированных статических библиотек. Тогда их не нужно будет ни компилировать ни индексировать. И Firebase, и AWS, и возможно другие тяжелые зависимости для каждого релиза добавляют XCFramework файлы которые легко подключить к проекту. Даже если XCFramework файла нет его можно собрать самому.
Пример. XCFramework файлы для Firebase можно скачать с GitHub на странице с релизами. Они лежат в файле Firebase.zip.
Чтобы было удобнее подключать и обновлять XCFramework файлы их можно обернуть в Swift пакет. Об этом подробно написано в документации от Apple:
Distributing Binary Frameworks as Swift Packages
Далее я покажу на примере Firebase Crashlytics и Analytics как создать Swift Package с бинарной зависимостью.
Создаём новый локальный Swift пакет и подключаем его к проекту.


Переносим в корневую директорию этого пакета необходимые XCFramework файлы. Например, если мы хотим подключить Crashyltics и Analytics к проекту, то нужно перенести XCFramework файлы из директорий FirebaseCrashlytics и FirebaseAnalytics.

В Package.swift файле в разделе targets удаляем весь код и добавляем ссылки на XCFramework файлы используя .binaryTarget . Далее указываем эти бинарные таргеты у библиотеки которую будем распространять. В итоге Package.swift файл будет выглядеть так:
// swift-tools-version: 5.6 import PackageDescription let package = Package( name: "FirebaseBinaries", platforms: [ .iOS(.v14) ], products: [ .library( name: "FirebaseBinaries", targets: [ "FirebaseAnalytics", "FirebaseCore", "FirebaseCoreDiagnostics", "FirebaseInstallations", "GoogleAppMeasurement", "GoogleDataTransport", "GoogleUtilities", "nanopb", "PromisesObjC", "FirebaseCrashlytics" ]) ], targets: [ .binaryTarget(name: "FirebaseAnalytics", path: "Frameworks/FirebaseAnalytics/FirebaseAnalytics.xcframework"), .binaryTarget(name: "FirebaseCore", path: "Frameworks/FirebaseAnalytics/FirebaseCore.xcframework"), .binaryTarget(name: "FirebaseCoreDiagnostics", path: "Frameworks/FirebaseAnalytics/FirebaseCoreDiagnostics.xcframework"), .binaryTarget(name: "FirebaseInstallations", path: "Frameworks/FirebaseAnalytics/FirebaseInstallations.xcframework"), .binaryTarget(name: "GoogleAppMeasurement", path: "Frameworks/FirebaseAnalytics/GoogleAppMeasurement.xcframework"), .binaryTarget(name: "GoogleDataTransport", path: "Frameworks/FirebaseAnalytics/GoogleDataTransport.xcframework"), .binaryTarget(name: "GoogleUtilities", path: "Frameworks/FirebaseAnalytics/GoogleUtilities.xcframework"), .binaryTarget(name: "nanopb", path: "Frameworks/FirebaseAnalytics/nanopb.xcframework"), .binaryTarget(name: "PromisesObjC", path: "Frameworks/FirebaseAnalytics/PromisesObjC.xcframework"), .binaryTarget(name: "FirebaseCrashlytics", path: "Frameworks/FirebaseCrashlytics/FirebaseCrashlytics.xcframework") ] )
Далее необходимо подключить к главному таргету библиотеку FirebaseBinaries . Выбираем таргет, и в разделе Frameworks, Libraries and Embedded Content нажимаем на + и выбираем FirebaseBinaries .

Кроме того, так как Firebase частично написан на C++, нужно подключить библиотеку libc++.tbd .

В файле Firebase.zip в README.md так же указано что нужно добавить флаг линковки
-ObjC

Все! Теперь можно использовать Crashlytics и Analytics в проекте.
Если не хотите держать Swift пакет с xcframework файлами рядом с проектом, то можете создать отдельный репозиторий, перенести туда этот пакет и подключить его к основному проекту указав адрес репозитория.
Результат
Время сборки простейшего приложения, после подключения тяжелой библиотеки, почти не изменилось. Было 0.4 сек стало 1 сек.

Время на индексацию никак не изменилось т.к. бинарная зависимость не индексируется.
Xcode больше не нагружает процессор при переключении веток.
Кол-во зависимостей увеличилось всего на одну — FirebaseBinaries.
Удовлетворенность разработчиков выросла. Больше никто не жалуется на то что Xcode тормозит, больно переключать ветки.
Минусы
Минус такого подхода один — станет сложнее обновлять зависимости. Необходимо скачивать zip файл с xcframework файлами и помещать их в Swift Package. К счастью это можно автоматизировать.
Вывод
Если вы хотите подключить к iOS проекту, через Swift Package Manager, зависимость содержащую огромное количество исходного кода, подключите её в скомпилированном виде, через XCFramework файл. Благодаря этому, скорость сборки вашего приложения не изменится, как и время на индексацию исходного кода, а самое главное, вы избежите багов Xcode при работе с SPM.
К сожалению, не доступен сервер mySQL
Создание Swift проекта с SPM (Swift Package Manager) Xcode
Swift Package Manager (SPM) – это, по сути конфиг, вашего проекта. Используя SPM у Вас нет необходимости использовать xcodeproj для работы с проектом, что в свою очередь позволяет запускать проекты не только на Mac, но и на Linux, где работать с xcodeproj на текущий момент невозможно.
Чтобы всё получилось необходимо установить Xcode, без этого никак. Также нужно базовое понимание команд, используемых в терминале (terminal).
Создание
Чтобы создать Swift проект с последующим использованием Swift Package Manager откройте терминал. Создайте директорию в нужном Вам месте. Название директории имеет значение, как назовёте директорию так и будет называться проект. Для удобства назовём директорию SpmProject.
Создание директории
mkdir SpmProject
Переход в директорию
cd SpmProject
Создание проекта с SPM
swift package init — type executable
Терминал уведомит, что был добавлен ряд файлов. Например, так как показан на скрине ниже.
Теперь интересует главное – как добавить зависимость? Открываем созданный файл «Package.swift» в любом редакторе (например, в Xcode).
Данный файл хранит в себе конфигурацию проекта, таргеты, зависимости. Добавим в качестве зависимости библиотеку Alamofire. Для этого в зависимости (dependencies) проекта нужно добавить ссылку откуда брать библиотеку. Затем, в конкретный таргет прописать зависимость. Выглядеть будет примерно следующим образом.
Если использовать в качестве редактора данного файла Xcode, то он сам подтянет зависимость. В противном случае для скачивания библиотеки потребуется выполнить команду в терминале.
AppCode 2020.2: поддержка Swift Package Manager, улучшенное быстродействие, Change Signature для Swift и многое другое
КПДВ — это Change Signature, уже пятый рефакторинг для Swift. Обо всем остальном в новом AppCode 2020.2 — ниже.

Поддержка Swift Package Manager
Мы сделали начальную поддержку SPM-зависимостей в проектах для Xcode, а именно:
- Отображение зависимостей в окне проекта: 
- Автодополнение, подсветку и навигацию для них, плюс некоторые специфичные для SPM-зависимостей возможности, такие как автодополнение таргетов в Package.swift: 
Быстродействие
Мы постоянно работаем над улучшением быстродействия IDE — к примеру, в прошлом релизе получилось ускорить повторное открытие проектов. В версии 2020.2 мы закончили большой кусок работы, переработав механизм индексации. В результате IDE должна стать в целом отзывчивее, а подвисания при открытии проекта или его перезагрузке (например, во время переключений бранчей) должны исчезнуть.
Change Signature
Изменение сигнатуры метода — частая операция. Обычно ее приходится делать с помощью нескольких последовательных рефакторингов переименования, после чего часть работы в любом случае придется делать руками (поменять область видимость, вручную переставить параметры местами и так далее). Теперь AppCode может выполнить все эти действия за один раз — достаточно вызвать рефакторинг Change Signature ( ⌘F6 ) на любом использовании метода. Если этот метод был перегружен, можно выбрать, необходимо ли выполнить рефакторинг на базовом методе и перегруженных или только на самом перегруженном методе:

В открывшемся диалоге можно переименовать метод, изменить внутренние и внешние имена параметров, а также добавить новые или удалить существующие параметры. При добавлении параметра можно выставить значение по умолчанию, а затем с помощью чекбокса Optional либо оставить значение в сигнатуре метода, либо вставить его во все вызовы метода:
Самое простое применение — просто поменять местами пару параметров ( ⌥↑ / ⌥↓ ):
Автодополнение
Одно из самых интересных изменений — это использование SourceKit в автодополнении. В прошлом релизе мы начали использовать его во время индексации, с этого релиза начинаем использовать и после нее, добавляя результаты SourceKit к результатам AppCode. В целом, это должно сделать автодополнение более корректным. Кроме этого, есть пачка небольших, но полезных улучшений:
- Автодополнение для get , set , didSet , и willSet теперь автоматически ставит курсор внутри тела выражения: 
- В режиме Smart теперь показываются конструкторы: 
- Заработало такое же, как в Objective-C, автодополнение имен шрифтов: 
- Trailing closures стали отображаться в списке автодополнения при наборе < : 
Инспекции
На смену старому индикатору инспекций пришел новый доработанный виджет, который отображает количество ошибок и предупреждений в текущем файле и позволяет между ними переключаться. По клику на него открывается окно Problems, отображающее все ошибки и предупреждения в текущем файле списком:

Также в нем можно изменить текущий уровень подсветки файла с помощью выпадающего списка Highlight…
Пулл-реквесты GitHub

Как и все IDE от JetBrains, AppCode получил полную поддержку пулл-реквестов GitHub, которая включает в себя следующие возможности:
- Отображение всей информации по пулл-реквесту (название, автор, переписка и др.) в отдельном окне
- Полная интеграция работы с ревью: теперь можно прямо из IDE начать или запросить ревью своих изменений, оставить комментарий или отправить изменения на ревью.
- Merge прямо из IDE.
На этом всё! Все вопросы и пожелания пишите прямо тут в комментариях — будем рады ответить!
Ваша команда AppCode
The Drive to Develop