
Swift Package Manager (SPM) — правильная модульная система для современного видеоприложения: видеочата, стриминга, записи или прямых трансляций. Он встроен в Xcode, нативно работает с границами конкурентности Swift 6 и в 2026 году остаётся единственным пакетным менеджером, который Apple рекомендует для нового кода под iOS. Сделано правильно — SPM удешевляет поддержку видеоприложения, ускоряет сборку и облегчает совместную работу команд над кодом. Сделано неправильно — превращается в ад из вложенных пакетов с пятнадцатиминутной разрешимостью зависимостей и хрупким CI.
Это руководство — плейбук, по которому Фора Софт настраивает Swift Package Manager в видеоприложениях, реально попадающих в продакшен: границы модулей, фиксация версий, интерфейсы пакетов, безопасные для Sendable, выбор зависимостей для WebRTC и стриминга, CI-пайплайны и конкретная структура продакшен-уровневого Package.swift. Если вы разрабатываете приложение для видеоконференций, меняете стриминговый стек или создаёте собственную обработку видео и аудио — эту модульную архитектуру стоит взять за основу.
Главное
• SPM — выбор по умолчанию для новых iOS-видеоприложений. Встроен в Xcode, понимает Swift 6, удобен из командной строки и остаётся единственным менеджером, в котором границы Sendable аккуратно переносятся между модулями. CocoaPods актуален только для интеграции с легаси-кодом.
• Разделяйте по границам ответственности, а не по функциональным возможностям. Один SPM-модуль на каждую actor-зону (Signalling, MediaEngine, CaptureSession, MediaFX, UI) — это защищает от утечек Sendable между модулями.
• Фиксируйте всё; обновляйте обдуманно. Держите Package.resolved в системе контроля версий, для тяжёлых Objective-C-бинарников (например, WebRTC) указывайте точный коммит, а обновления зависимостей планируйте раз в квартал, а не делайте спонтанно по ходу дела.
• Бинарные таргеты ускоряют сборку WebRTC в десять раз. Распространяйте iOS-фреймворк Google WebRTC в виде бинарного .xcframework, а не собирайте из исходников. Чистая сборка в CI сокращается с десятков минут до менее чем шестидесяти секунд.
• Не дробите слишком сильно. Подпакеты увеличивают стоимость разрешения зависимостей и усложняют CI. Для видеоприложения оптимальное число модулей — 6–10; всё, что больше 15, — как правило, признак случайной фрагментации.
Почему этот плейбук написала Фора Софт
Фора Софт выпускает видео- и аудиоприложения для iOS с 2005 года — этого времени достаточно, чтобы помнить CocoaPods, Carthage и все три итерации SPM до того, как он стал зрелым. Мы используем SPM в продакшене в обучающей платформе BrainCert, корпоративном видеоинструменте VALT для проверки записей, видеоэффектах SuperPower FX и приложениях-спутниках для Smart TV в проекте bellicon.
Этот плейбук — модульная архитектура, которую мы используем каждый день. Мы отработали её на миграциях с CocoaPods на SPM, с Swift 5 на Swift 6 и при переходе от одномодульных монолитов к правильно разделённым пакетам. Подход Agent Engineering (старшие инженеры, контролирующие Cursor/Claude/Copilot) помогает держать рефакторинг под SPM в рамках бюджета и обычно обеспечивает экономию 20–40% по сравнению со стандартными ставками агентств, потому что AI-ассистент особенно эффективен в выполнении рутинных задач при модульной миграции.
Запутались в настройке Swift Package Manager?
Медленные сборки, запутанные зависимости, утечки Sendable между модулями? Пришлите нам ваш Package.swift и примерный список модулей. Мы вернёмся с чистой архитектурой и точной оценкой.
SPM, CocoaPods и Carthage — что выбрать в 2026 году
Для нового видеоприложения единственный разумный выбор — SPM. Для старого приложения миграцию с CocoaPods можно запланировать, но не спешить: CocoaPods на момент публикации всё ещё работает и поддерживается.
| Параметр | SPM | CocoaPods | Carthage |
|---|---|---|---|
| Интеграция с Xcode | Нативная | Через плагин | Вручную |
| Конкурентность Swift 6 | Модульные swiftSettings |
Только обходные пути | Только обходные пути |
| Бинарные таргеты | XCFramework | Подключаемый бинарь | Да |
| Производительность CI | Хорошая (с кэшем) | Неровная | Быстрая для собранных артефактов |
| Приватные репозитории | Нативно SSH / HTTPS | Приватный репозиторий спецификаций | Git-URL |
| По умолчанию для нового iOS-приложения | Да | Только легаси | Редко |
Берите SPM, когда: начинаете новый iOS-проект, переносите старое приложение на Swift 6 или хотите упростить управление зависимостями в нескольких командах. Серьёзных причин использовать CocoaPods в новых проектах больше нет.
Раскладка модулей для видеоприложения в продакшене
Ниже — структура, которую мы используем для видеочата или стриминг-приложения на Swift 6. Каждый модуль привязан к своей зоне конкурентности, минимизирует публичный интерфейс и делает границы `Sendable` проверяемыми на этапе компиляции.
MyVideoApp/
Package.swift
Sources/
Core/ // Sendable types, errors, logging (zero deps)
Signalling/ // WebSocket + protocol (depends: Core)
Networking/ // REST / auth (depends: Core)
MediaEngine/ // WebRTC wrapper actor (depends: Core, WebRTCBinary)
CaptureSession/ // AVCaptureSession actor (depends: Core)
MediaFX/ // Core ML, filters (depends: Core)
CallFlow/ // orchestration actor (depends: all above)
UIKitVideoView/ // UIViewRepresentable + Metal (depends: Core)
Features/ // SwiftUI feature slices (depends: CallFlow, UIKitVideoView)
BinaryFrameworks/
WebRTC.xcframework
Tests/
...
App/ // the Xcode project (depends on Features)
Xcode-проект уровня приложения зависит только от Features; никакому коду интерфейса или связке не нужно знать о WebRTC или Core ML. Бинарные таргеты (WebRTC) находятся за пределами Sources/ и подключаются через манифест пакета.
Базовый Package.swift
Ниже — каркас манифеста, с которого мы начинаем. Swift Tools 6.0, индивидуальные swiftSettings на модуль, явный бинарный таргет и сквозная проверка Sendable.
// swift-tools-version: 6.0
import PackageDescription
let swift6Settings: [SwiftSetting] = [
.enableExperimentalFeature("StrictConcurrency"),
.enableUpcomingFeature("InferSendableFromCaptures"),
]
let package = Package(
name: "MyVideoApp",
platforms: [.iOS(.v17)],
products: [
.library(name: "Features", targets: ["Features"]),
],
dependencies: [
.package(url: "https://github.com/google/GoogleWebRTC", exact: "125.6422.06"),
// pin every external dep to `exact` in production
],
targets: [
.binaryTarget(
name: "WebRTC",
path: "BinaryFrameworks/WebRTC.xcframework"
),
.target(name: "Core", swiftSettings: swift6Settings),
.target(
name: "MediaEngine",
dependencies: ["Core", "WebRTC"],
swiftSettings: swift6Settings
),
// ...repeat per module
.testTarget(
name: "MediaEngineTests",
dependencies: ["MediaEngine"],
swiftSettings: swift6Settings
),
]
)
Берите бинарные таргеты, когда: зависимость долго собирается из исходников (WebRTC, FFmpeg, TensorFlow Lite), распространяется как закрытый бинарник или почти не меняется. Всё остальное компилируйте из исходников — тогда стектрейсы и отладка останутся понятными.
Sendable-безопасные интерфейсы модулей
Чистота Sendable между модулями — то, что делает сборку Swift 6 быстрой, а не запутанной. Интерфейсы остаются аккуратными, если соблюдать три правила:
1. Публичные типы — только в Core. Модуль Core содержит все типы-значения, помеченные как Sendable, которые используются в других модулях: ID, ошибки, статистика, состояния. Никакой другой модуль не может экспортировать Sendable-типы.
2. API акторов возвращают значения, которые можно отправить. Когда MediaEngine передаёт состояние UI-слою, он возвращает снапшоты Core.CallState, а не внутренний RTCPeerConnection.
3. sending-параметры через границы модулей. Буферы, payload’ы и одноразовые объекты передаются как sending. Вызывающая сторона описывает, что передаёт владение объектом, а компилятор гарантирует выполнение этого правила.
Каталог SPM-зависимостей для видеоприложений
За десять лет работы над видеоприложениями мы выработали короткий список проверенных зависимостей, которым доверяем в 2026 году. Несколько хорошо подобранных пакетов лучше длинного Package.resolved.
1. Google WebRTC. Бинарный XCFramework. Привязывайте к точному тегу в исходниках. Обновляйте раз в квартал.
2. Альтернативы WebRTC под Apple. LiveKit Swift SDK — если вы используете LiveKit Cloud или самохостинг LiveKit SFU; команда поддерживает полноценный SPM-таргет.
3. Starscream или URLSessionWebSocketTask. WebSocket-клиент для сигналинга. Starscream проверен временем; URLSession не добавляет лишних зависимостей и подойдёт, если вам не нужны расширения.
4. Swift Collections и Swift Algorithms. Коллекции, близкие к стандартным (OrderedSet, Deque), и ленивые алгоритмы. Риск отсутствует.
5. swift-log. Структурированное логирование с возможностью менять бэкенды. Одной строкой можно переключиться с вывода в консоль в разработке на OSLog или удалённый бэкенд в продакшене.
6. Kingfisher или SDWebImage. Используйте только при необходимости агрессивного кэширования изображений — например, аватаров в большом масштабе. Для простых задач достаточно AsyncImage и небольшого собственного кэша.
7. Core ML + Vision (без зависимостей). Вся обработка ИИ на устройстве в 2026 году строится на фреймворках Apple; сторонняя обёртка не даёт достаточной ценности, чтобы оправдать риски.
Нужна помощь, чтобы подчистить Package.resolved?
Мы регулярно уменьшаем дерево SPM-зависимостей в продакшен-видеоприложениях на 30–60% без потери функциональности. Пришлите ваш манифест — мы вернёмся со списком зависимостей для удаления и оценкой выигрыша по времени сборки.
CI и кэширование сборки — что реально ускоряет билды
Время CI у видеоприложения на SPM определяют три рычага:
1. Бинарный WebRTC. Поставка WebRTC в виде XCFramework сокращает время сборки с 15–25 минут (из исходников) до менее чем минуты (бинарник). Это самая эффективная разовая оптимизация CI.
2. Кэш SPM в CI. Кэшируйте каталог .build и ~/Library/Caches/org.swift.swiftpm на CI-раннере. GitHub Actions, Bitrise и Xcode Cloud позволяют настроить это менее чем за 15 строк кода.
3. Инкрементальные сборки и Explicit Module Builds. Xcode 15+ поддерживает явные модульные сборки. На больших проектах они значительно ускоряют инкрементальные сборки — включайте эту опцию для таргета.
В сумме грамотно настроенный CI-пайплайн для среднего видеоприложения укладывается в «тест + архив» за 10 минут — этого достаточно для нескольких релизов в день, к которым стремятся современные продуктовые команды.
Версионирование, пиннинг и квартальные обновления зависимостей
В продакшен-видеоприложениях мы фиксируем каждую внешнюю зависимость к точной версии, коммитим Package.resolved и обновляем зависимости по ежеквартальному графику, а не через случайные PR’ы. Исключение — патчи безопасности; они проходят вне очереди.
Для бинарников (WebRTC, FFmpeg, модели Core ML) фиксируем точный SHA архива .xcframework. Расхождение бинарников — самый неприятный вид багов в продакшене, потому что они тихо пересобираются на новом CI-раннере, и тесты не успевают поймать изменения.
Берите пиннинг к точной версии, когда: зависимость используется в рабочем пайплайне продакшена. Ослабляйте до .upToNextMinor только для внутренних утилит на чистом Swift, где критические изменения случаются редко и не влияют на работу.
Тестирование видеоприложения на Swift Package Manager
Нативный testTarget в SPM хорошо работает со Swift Testing. Для каждого модуля создаётся отдельный тест-таргет, @Suite — для ключевых сценариев, параметризованные тесты — для матричного покрытия.
Интеграционные и UI-тесты размещайте на уровне Xcode-проекта (XCUITest), а не внутри SPM: тесты SPM по умолчанию запускаются без устройства и не подходят для проверки взаимодействий с UIKit или SwiftUI. Общий подход к QA описан в отдельной статье о процессе тестирования в Фора Софт.
Миграция с CocoaPods на SPM без поломки сборки
Безопасный путь миграции — в три этапа:
Фаза 1 — двойной резолв. Оставляем Podfile. Добавляем Package.swift с первым модулем (обычно Core). Убеждаемся, что Xcode и CI собирают оба резолвера без ошибок.
Фаза 2 — переносим зависимости по одной. Каждую зависимость CocoaPod переводим на SPM за один релиз-цикл, начиная с чисто Swift-библиотек. Зависимости на Objective-C (например, WebRTC или помощники для AVFoundation) обычно требуют упаковки в XCFramework — их стоит планировать на конец.
Фаза 3 — удаляем Podfile. Только после того, как последний под исчезнет и полноценный релиз выйдет в стор исключительно под SPM. Не торопитесь: возможность откатиться на CocoaPods во время миграции — это дешёвый способ подстраховаться.
Безопасность, цепочка поставок и пакеты, которые вы реально проверяете
Каждая зависимость в SPM — это код, который вы передаёте пользователю. Для видеоприложения, работающего со сквозным шифрованием, пользовательским медиа или контентом, подпадающим под HIPAA, относитесь к дереву зависимостей как к активу безопасности.
1. Проверяйте каждую зависимость перед подключением. Лицензия, мейнтейнер, дата последнего коммита, открытые предупреждения по безопасности. Если хотя бы один пункт вызывает сомнения — ищите альтернативу или вендорьте только нужный кусок кода.
2. Подписывайте и проверяйте бинарные таргеты. SPM умеет работать с подписанными контрольными суммами XCFramework — используйте их для каждой бинарной зависимости. Тихая подмена бинарника на вашем хостинге — это атака на цепочку поставок.
3. Следите за CVE. Подпишитесь на обновления CVE для каждой зависимости или настройте автоматизацию с помощью Dependabot / Renovate — они будут создавать pull request’ы в ваш Package.swift.
Берите внутренний вендоринг зависимости, когда: upstream заброшен, критический CVE не исправлен или ваш скоуп соответствия (HIPAA, SOC 2) требует аудируемого форка, который вы контролируете сами.
Экономика — бюджеты рефакторинга под SPM
Ориентировочные цифры для продакшен-видеоприложений по размеру кодовой базы. Конкретные значения зависят от количества зависимостей и объёма Objective-C-моста.
1. Небольшое iOS-видеоприложение (<50 тыс. строк, 5–8 зависимостей). Переход с CocoaPods на SPM займёт 1–2 инженерные недели. Разделение на модули — ещё 1–2 недели.
2. Среднее приложение (50–200 тыс. строк, 10–20 зависимостей). Миграция займёт 3–5 недель. Полное разделение по зонам конкурентности — ещё 4–6 недель.
3. Крупное приложение (200 тыс.+ строк, 25+ зависимостей, тяжёлый ObjC). Миграция займёт 8–12 недель и потребует двух релизов. Модульная работа — отдельный подпроект (12–20 недель), обычно совпадающий с переходом на Swift 6.
4. Упаковка бинарного таргета для внутреннего SDK. 1–2 недели — на создание SDK с первичным XCFramework и настройку пайплайна релиза. Последующие квартальные обновления занимают один рабочий день.
Agent Engineering сокращает каждую из этих оценок на 20–40% при чётко описанной миграции. За точной оценкой по вашему репозиторию — позвоните или напишите нам.
Мини-кейс — рефакторинг под SPM в корпоративном видеоприложении
Ситуация. Корпоративное видеоприложение с 20+ подами в CocoaPods, монолит на один модуль, чистая CI-сборка длится 20 минут и миграция на Swift 6 вызывает всё больше проблем. Цель: перейти на SPM, разбить модули по зонам ответственности, сократить чистую CI-сборку до менее чем 10 минут и не нарушить ритм релизов.
План на 12 недель. Недели 1–3: создаём каркас Package.swift, выделяем модуль Core, настраиваем двойной резолв вместе с текущим Podfile. Недели 4–7: постепенно переносим чисто-Swift зависимости по релизам (перенесено 7 подов). Недели 8–9: WebRTC упаковываем в XCFramework, выделяем модули MediaEngine, CaptureSession и Signalling. Недели 10–11: убираем Podfile, настраиваем кэш в CI, включаем конкурентность Swift 6 по модулям. Неделя 12: релиз и ретроспектива.
Итог. Чистая CI-сборка сократилась с ~20 минут до менее чем 8. Новые границы модулей при переходе на Swift 6 выявили три скрытые проблемы с конкурентностью, которые раньше незаметно попадали в релизы. Скорость разработки фич выросла: теперь команда может параллельно работать над MediaEngine и Features, не мешая друг другу.
Фреймворк принятия решений — спланируйте свой SPM за пять вопросов
1. Greenfield или легаси? Greenfield — SPM с самого начала. Легаси — планируйте переход на два-три релиза, а не за один большой спринт.
2. Swift 6 или ещё Swift 5? SPM-модули по зонам конкурентности дают наибольший эффект на Swift 6. Если вы уже используете Swift 5.10 с полной проверкой, то и в этом случае вы получаете значительную пользу.
3. WebRTC бинарём или из исходников? Бинарь — ради скорости CI; исходники — только если нужен свой патч или вы используете не-LTS-ветку WebRTC.
4. Зависимость открытая или приватная? Открытые зависимости подключаются в основном файле Package.swift. Приватные хранятся в отдельном приватном репозитории с настроенным SSH-доступом в CI.
5. Кто отвечает за апгрейды зависимостей? Каждый квартал назначайте одного инженера «уборщиком зависимостей»: он разбирает накопленные обновления, изучает changelog и проводит апгрейд через PR. Ротация — раз в квартал.
Грабли, которых стоит избегать
1. Один модуль на каждую фичу. Модули по фичам выглядят аккуратно, но обычно приводят к циклическим зависимостям. Делите код по зонам конкурентности или архитектурным слоям; фичи — это папки внутри этих модулей.
2. Сборка WebRTC из исходников в CI. Если вам не нужна собственная ветка, используйте готовый XCFramework. Ежедневная экономия времени в CI очень существенна.
3. .upToNextMajor на всё подряд. Открытые диапазоны версий в продакшене гарантируют сюрпризы посреди релиза. Фиксируйте версии точно, обновляйте осознанно.
4. Постоянное сосуществование SPM и CocoaPods. Двойной резолв допустим на этапе миграции. Если через год вы всё ещё используете оба — Podfile превратился в кладбище зависимостей.
5. Игнор кэша пакетов в CI. Без кэша каждый PR пересобирает все зависимости с нуля. Пятнадцать строк в CI-конфиге — самая дешёвый способ ускорить сборку.
KPI — что измерять после рефакторинга под SPM
KPI качества. Количество вхождений @unchecked Sendable (чем меньше — тем лучше), межмодульные предупреждения Swift 6 в CI (цель — ноль) и число зависимостей на критическом пути.
Бизнес-метрики. Время разработки фичи до и после разделения модулей, задержка между «CI прошёл» и деплоем, а также время адаптации новых инженеров (должно снижаться по мере того, как структура модулей становится понятной).
KPI надёжности. Время полной CI-сборки, время инкрементальной CI-сборки и частота вопросов вроде «почему у меня упал билд» в чате. Все три показателя должны заметно снизиться после правильной настройки SPM.
Когда не стоит затевать SPM-рефакторинг в этом квартале
SPM — правильный выбор для большинства iOS-видеоприложений, но не всегда сейчас. Отложите переход, если команда в процессе миграции на Swift 6 или SwiftUI (лучше делать одну миграцию за раз), если продукт заморожен из-за комплаенса или ревью в App Store, либо если релизный ритм и так напряжён, а CI едва стабилен.
В этих случаях планируйте рефакторинг под SPM как первый проект следующего квартала. Он окупается быстрее всего, если начать с стабильной базы, а не во время другой миграции.
Готовы перестроить видеоприложение вокруг Swift Package Manager?
Мы переводили iOS-приложение для видеостриминга с монолитной архитектуры на CocoaPods на Swift 6 и SPM. Пришлите репозиторий и целевую версию — подготовим поэтапный план миграции.
FAQ
Готов ли Swift Package Manager для видеоприложений в продакшене?
Да. SPM встроен в Xcode начиная с Xcode 11, стабилен под Swift 5+, а в Swift 6 он стал единственным менеджером, который отслеживает границы конкурентности на уровне модулей. Мы используем его в продакшене в нескольких выпущенных видеоприложениях.
Сколько модулей — уже слишком много?
Для типичного видеоприложения оптимальное количество — 6–10 модулей. После 15 обычно наступает убывающая отдача и растут накладные расходы CI. Делите код по зонам конкурентности или архитектурным слоям, а не по отдельным функциям.
Может ли SPM заменить CocoaPods для легаси-кода на Objective-C?
Да, но работы заметно больше. Зависимости на Objective-C нужно упаковать в XCFramework или аккуратно настроить module-map. Если кода на Objective-C много, миграцию лучше распределить на два-три релиза, а не пытаться сделать за один спринт.
Как управлять приватными SPM-пакетами?
Храните их в приватном Git-репозитории, подключайтесь по SSH или HTTPS с токеном, настройте учётные записи для CI. Центральный реестр не нужен. Для крупных организаций отдельный репозиторий «общих пакетов» помогает находить внутренние SDK, не засоряя Package.swift каждого приложения.
Нужно ли самим собирать бинарь Google WebRTC?
Google публикует официальный XCFramework. Заведите его зеркало в своём хранилище (S3 или GCS) и подключайте именно этот URL из Package.swift — так ваша сборка не зависит от изменений upstream. Пиньте всегда к точному SHA.
Как SPM влияет на время сборки больших видеоприложений?
С бинарным WebRTC, CI-кэшем и явными модульными сборками среднее видеоприложение обычно собирает чистую сборку меньше чем за 10 минут, а инкрементальную сборку для PR — меньше чем за 3. Монолитный Xcode-проект с CocoaPods обычно требует в 2–3 раза больше времени.
Как выглядит SPM-проект с Фора Софт?
Обычно на этап discovery уходит 2–3 недели: аудит репозитория, предложение структуры модулей, определение порядка миграции. Далее выделенная команда работает в течение двух-трёх ваших стандартных релизных циклов, выпуская рефакторинг. Часто мы работаем с клиентами как отдельная команда разработки.
Подходит ли SPM для Smart TV и tvOS-видеоприложений?
Да. Таргеты SPM поддерживают tvOS как платформу, и та же структура модулей переносится с iOS на tvOS с минимальными правками (ввод, движок фокуса). Мы используем такой подход в продакшене в Smart TV-приложениях — например, в проекте bellicon Smart TV.
Что читать дальше
Язык
Swift 6: что важно знать
Языковой партнёр SPM — конкурентность, Sendable, sending, типизированные throws.
Видео
Swift 6 для разработки видеочата на iOS
Прикладной взгляд на SPM + Swift 6 для видеочата в iOS
UI
SwiftUI vs UIKit для видеоконференций
Где SwiftUI выигрывает, а где UIKit ещё остаётся полезным в видеоприложении.
Интероп
Интеграция SIP в видеоконференцсвязь
Когда PSTN и устаревшие SIP-эндпоинты подключаются к вашей современной SFU на SPM.
Контекст
Технический дайджест лета 2025
iOS 26, Swift на Android, GPT-5 — релизный контекст вокруг Swift 6 и SPM.
Готовы заставить Swift Package Manager работать на ваше видеоприложение?
SPM — модульная система по умолчанию для современной iOS-видеоразработки: встроена в Xcode, поддерживает Swift 6, работает с CI и остаётся единственным менеджером, где границы конкурентности легко переносить между модулями. Делите код по зонам конкурентности, чётко указывайте зависимости, поставляйте WebRTC как бинарник, кэшируйте сборку в CI — и получите проект, который быстро компилируется, удобно просматривать на ревью и легко масштабировать между командами.
Если вы разрабатываете новое видеоприложение или переносите монолит с CocoaPods на SPM и Swift 6, этот плейбук — тот, по которому мы работаем каждый день. Когда понадобятся инженеры, уже выпускавшие такую архитектуру в продакшен, мы — в одном звонке или письме.
Хотите применить SPM-плейбук к своему репозиторию?
Напишите нам, сколько у вас зависимостей, сколько длится сборка в CI и в каком статусе находится поддержка Swift 6. Мы подготовим поэтапный план рефакторинга и обоснованную оценку.
