<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agapov Alexey</title><link>https://agapovone.github.io/</link><description>Recent content on Agapov Alexey</description><generator>Hugo -- gohugo.io</generator><language>ru-ru</language><lastBuildDate>Tue, 10 Jan 2023 17:18:00 +0500</lastBuildDate><atom:link href="https://agapovone.github.io/index.xml" rel="self" type="application/rss+xml"/><item><title>Resources</title><link>https://agapovone.github.io/articles/resources/</link><pubDate>Tue, 10 Jan 2023 17:18:00 +0500</pubDate><guid>https://agapovone.github.io/articles/resources/</guid><description>
&lt;h3 id="-крутые-авторы-и-разработчики-в-твиттере">
&lt;a href="#-%d0%ba%d1%80%d1%83%d1%82%d1%8b%d0%b5-%d0%b0%d0%b2%d1%82%d0%be%d1%80%d1%8b-%d0%b8-%d1%80%d0%b0%d0%b7%d1%80%d0%b0%d0%b1%d0%be%d1%82%d1%87%d0%b8%d0%ba%d0%b8-%d0%b2-%d1%82%d0%b2%d0%b8%d1%82%d1%82%d0%b5%d1%80%d0%b5" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
🐦 Крутые авторы и разработчики в твиттере
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://twitter.com/mxcl">mxcl&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/mattt">mattt nshipster&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/chriseidhof">chriseidhof&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/bohdan_orlov">bohdan_orlov&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/johnsundell">johnsundell&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/icanzilb">icanzilb&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/merowing_">merowing&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/_inside">inside&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/twannl">twannl&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/seanallen_dev">seanallen_dev&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/daveverwer">daveverwer&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://twitter.com/twostraws">twostraws&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.twitter.com/pointfreeco">pointfree&lt;/a> и отдельно &lt;a href="https://www.twitter.com/mbrandonw">brandon&lt;/a>, &lt;a href="https://www.twitter.com/stephencelis">stephen&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="-периодические-материалы">
&lt;a href="#-%d0%bf%d0%b5%d1%80%d0%b8%d0%be%d0%b4%d0%b8%d1%87%d0%b5%d1%81%d0%ba%d0%b8%d0%b5-%d0%bc%d0%b0%d1%82%d0%b5%d1%80%d0%b8%d0%b0%d0%bb%d1%8b" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
📫 Периодические материалы
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://podlodka.io/">podlodka.io&lt;/a> — подкаст обо всём&lt;/li>
&lt;li>&lt;a href="https://iosdevweekly.com/">iosdevweekly.com&lt;/a> — недельный дайджест мира iOS/macOS разработки&lt;/li>
&lt;/ul>
&lt;h3 id="-блоги">
&lt;a href="#-%d0%b1%d0%bb%d0%be%d0%b3%d0%b8" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
✍️ Блоги
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://objc.io/">objc.io&lt;/a> — статьи и видео&lt;/li>
&lt;li>&lt;a href="https://www.swiftbysundell.com/">swiftbysundell.com&lt;/a> — блог и подкаст, хорошие статьи по базовым материалам&lt;/li>
&lt;li>&lt;a href="https://nshipster.com/">nshipster.com&lt;/a> — блог с глубокими знаниями и разбором от инженера Apple&lt;/li>
&lt;li>&lt;a href="https://www.avanderlee.com/">avanderlee.com&lt;/a> — блог с хорошим контентом на разные темы.&lt;/li>
&lt;li>&lt;a href="https://www.pointfree.co/">pointfree.co&lt;/a> — видео, есть бесплатные. Функциональные подходы.&lt;/li>
&lt;li>&lt;a href="https://useyourloaf.com/">useyourloaf.com&lt;/a> — отличный блог и книга про AutoLayout. Контент книги = статьи из блога.&lt;/li>
&lt;li>&lt;a href="https://refactoring.guru/ru">refactoring.guru/ru&lt;/a> - отличный сайт, есть книга. Паттерны ООП, разбор рефакторинга, примеры кода.&lt;/li>
&lt;li>&lt;a href="https://www.hackingwithswift.com/">hackingwithswift.com&lt;/a> — хороший контент. Первый выбор для поиска типичных решений вместо stackoverflow&lt;/li>
&lt;/ul>
&lt;h3 id="-статьи">
&lt;a href="#-%d1%81%d1%82%d0%b0%d1%82%d1%8c%d0%b8" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
📰 Статьи
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://medium.com/ios-os-x-development/ios-architecture-patterns-ecba4c38de52">Поверхностный обзор архитектур&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://habr.com/ru/company/oleg-bunin/blog/437584/">Математические основы AutoLayout&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://rambo.codes/posts/2020-02-20-mvc-with-sugar">MVC, и как его можно улучшить&lt;/a>. Отличная статья про FlowController, Child controllers.&lt;/li>
&lt;li>&lt;a href="https://github.com/onmyway133/blog/issues/106">FlowController против Coordinator&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://mokacoding.com/blog/swift-test-doubles/">Test doubles in Swift: dummies, fakes, stubs, and spies.&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="-книги">
&lt;a href="#-%d0%ba%d0%bd%d0%b8%d0%b3%d0%b8" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
📖 Книги
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://www.objc.io/books/app-architecture/">App Architecture&lt;/a> – книга по архитектурам с примерами&lt;/li>
&lt;li>&lt;a href="https://www.objc.io/books">Книги на objc.io&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://store.raywenderlich.com/">Разные книги с крутым контентом&lt;/a> Для начала в iOS хорошая iOS Apprentice.&lt;/li>
&lt;li>&lt;a href="https://flight.school/">Книги от Mattt (NSHipster)&lt;/a> для углубленного изучения&lt;/li>
&lt;/ul>
&lt;h3 id="-swift">
&lt;a href="#-swift" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
🧡 Swift
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://swiftbook.ru/content/docs/">Перевод&lt;/a> официальной книги &lt;a href="https://developer.apple.com/swift/">Swift от Apple&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://www.whatsnewinswift.com/">Сравнение фич swift по-версиям&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.apple.com/documentation/swift/swift_standard_library">Стандартная библиотека Swift&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="-wwdc-must-watch">
&lt;a href="#-wwdc-must-watch" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
🍏 WWDC must watch
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://developer.apple.com/videos/play/wwdc2018/417/">Testing Tips &amp;amp; Tricks&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.apple.com/videos/play/wwdc2018/416/">iOS Memory Deep Dive&lt;/a>&lt;/li>
&lt;li>&lt;a href="https://developer.apple.com/videos/play/wwdc2018/414/">Understanding Crashes and Crashlogs&lt;/a>&lt;/li>
&lt;/ul>
&lt;h3 id="-видео">
&lt;a href="#-%d0%b2%d0%b8%d0%b4%d0%b5%d0%be" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
🎥 Видео
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=A1vzcxR-Ss0">MVC is not your problem&lt;/a> — доклад о том, как делать MVC, не делая его massive.&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=J8u-tIt5wo4">Правильный MVC&lt;/a> — как справиться с MVC. Доклад от Redmadrobot&lt;/li>
&lt;li>&lt;a href="https://www.youtube.com/watch?v=7HtE3Ci78nU">Как работать с сетью правильно&lt;/a> — доклад от Сбербанка. URLSession и правильные подходы к работе с сетью.&lt;/li>
&lt;/ul>
&lt;h3 id="-хранилища-знаний">
&lt;a href="#-%d1%85%d1%80%d0%b0%d0%bd%d0%b8%d0%bb%d0%b8%d1%89%d0%b0-%d0%b7%d0%bd%d0%b0%d0%bd%d0%b8%d0%b9" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
📚 Хранилища знаний
&lt;/h3>
&lt;ul>
&lt;li>&lt;a href="https://tlroadmap.io/">TeamLead roadmap&lt;/a> – как развиваться самому в качестве тимлида&lt;/li>
&lt;/ul></description></item><item><title>Swift Package Manager in 2021</title><link>https://agapovone.github.io/articles/swiftpm/</link><pubDate>Sun, 25 Jul 2021 10:40:20 +0500</pubDate><guid>https://agapovone.github.io/articles/swiftpm/</guid><description>
&lt;h2 id="вопросы">
&lt;a href="#%d0%b2%d0%be%d0%bf%d1%80%d0%be%d1%81%d1%8b" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Вопросы
&lt;/h2>
&lt;ul>
&lt;li>Почему стоит уйти с Cocoapods?&lt;/li>
&lt;li>Как интегрировать пакеты? Что со сборкой на CI?&lt;/li>
&lt;li>Как структурировать пакеты в SwiftPM&lt;/li>
&lt;li>Как удобно разрабатывать отдельный пакет&lt;/li>
&lt;li>Как удобно выделять модуль для приложением&lt;/li>
&lt;li>Как выпускать версии пакетов и библиотек&lt;/li>
&lt;/ul>
&lt;h2 id="почему-стоит-уйти-с-cocoapods">
&lt;a href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d1%81%d1%82%d0%be%d0%b8%d1%82-%d1%83%d0%b9%d1%82%d0%b8-%d1%81-cocoapods" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Почему стоит уйти с Cocoapods
&lt;/h2>
&lt;p>SwiftPM поддерживает всё, что поддерживает Cocoapods и даже чуть больше.&lt;/p>
&lt;h3 id="поддержка-ресурсов">
&lt;a href="#%d0%bf%d0%be%d0%b4%d0%b4%d0%b5%d1%80%d0%b6%d0%ba%d0%b0-%d1%80%d0%b5%d1%81%d1%83%d1%80%d1%81%d0%be%d0%b2" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Поддержка ресурсов
&lt;/h3>
&lt;p>Работает, начиная с Xcode 12 и Swift 5.3.&lt;/p>
&lt;p>Ресурсы не обязательно объявлять в Package.swift файле. Например, asset catalogs, xib/storyboard, .lproj файлы будут включены в Bundle автоматически.&lt;/p>
&lt;blockquote>
&lt;p>When you add a resource to your Swift package, Xcode detects common resource types for Apple platforms and treats them as a resource automatically. For example, you don’t need to make changes to your package manifest for the following resources:&lt;/p>
&lt;ul>
&lt;li>Interface Builder files; for example, XIB files and storyboards&lt;/li>
&lt;li>Core Data files; for example, xcdatamodeld files&lt;/li>
&lt;li>Asset catalogs&lt;/li>
&lt;li>.lproj folders you use to provide localized resources&lt;/li>
&lt;/ul>
&lt;p>&lt;cite>&lt;a href="https://developer.apple.com/documentation/swift_packages/bundling_resources_with_a_swift_package">Bundling resources&lt;/a>&lt;/cite>&lt;/p>
&lt;/blockquote>
&lt;p>Использовать ресурсы в коде стало проще. Внутри Swift Package Manager пакета мы можем написать просто &lt;code>Bundle.module&lt;/code>.&lt;/p>
&lt;p>&lt;code>UIImage(named: &amp;quot;some_image&amp;quot;, bundle: .module)&lt;/code>&lt;/p>
&lt;p>Это работает благодаря флагу SWIFT_PACKAGE, который доступен внутри пакета.&lt;/p>
&lt;p>Пример добавления ресурсов описан в статье &lt;a href="https://useyourloaf.com/blog/add-resources-to-swift-packages/">useyourloaf&lt;/a>.&lt;/p>
&lt;p>Подробнее в видео &lt;a href="https://developer.apple.com/wwdc20/10169">WWDC20. Swift packages: Resources and localization&lt;/a>&lt;/p>
&lt;h3 id="swiftgen-rswift">
&lt;a href="#swiftgen-rswift" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
SwiftGen, R.swift
&lt;/h3>
&lt;p>Работает через Bundle.module.&lt;/p>
&lt;p>Нет явных шагов для билда ресурсов, как и с обычными пакетами. Кажется, имеет смысл держать рядом папку scripts с cli. И запускать периодически в терминале.&lt;/p>
&lt;h3 id="умеет-в-версионирование">
&lt;a href="#%d1%83%d0%bc%d0%b5%d0%b5%d1%82-%d0%b2-%d0%b2%d0%b5%d1%80%d1%81%d0%b8%d0%be%d0%bd%d0%b8%d1%80%d0%be%d0%b2%d0%b0%d0%bd%d0%b8%d0%b5" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Умеет в версионирование
&lt;/h3>
&lt;ul>
&lt;li>Выбор версии до следующей мажорной (95% использования)&lt;/li>
&lt;li>Конкретная версия&lt;/li>
&lt;li>Конкретная ветка&lt;/li>
&lt;/ul>
&lt;h3 id="поддерживает-binary-frameworks">
&lt;a href="#%d0%bf%d0%be%d0%b4%d0%b4%d0%b5%d1%80%d0%b6%d0%b8%d0%b2%d0%b0%d0%b5%d1%82-binary-frameworks" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Поддерживает binary frameworks
&lt;/h3>
&lt;p>Поддержка xcframework нужна для библиотек, у которых мы хотим закрыть исходный код. Или, например, у KMM библиотеки, которая нужна как зависимость.&lt;/p>
&lt;p>Такие фреймворки ограничены только Apple платформой и теми архитектурами, под которые они собраны. Это может вызывать проблемы при разработке.&lt;/p>
&lt;p>Подробнее об этих фреймоврках и интеграции в Swift Package Manager в &lt;a href="https://developer.apple.com/wwdc20/10147">видео Distribute binary frameworks as Swift Packages&lt;/a> и &lt;a href="https://developer.apple.com/documentation/swift_packages/distributing_binary_frameworks_as_swift_packages">документации о Binary Frameworks&lt;/a>&lt;/p>
&lt;h2 id="интеграция-пакетов-и-сборка-на-ci">
&lt;a href="#%d0%b8%d0%bd%d1%82%d0%b5%d0%b3%d1%80%d0%b0%d1%86%d0%b8%d1%8f-%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d0%be%d0%b2-%d0%b8-%d1%81%d0%b1%d0%be%d1%80%d0%ba%d0%b0-%d0%bd%d0%b0-ci" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Интеграция пакетов и сборка на CI
&lt;/h2>
&lt;h3 id="внешние-пакеты-внутри-приложения">
&lt;a href="#%d0%b2%d0%bd%d0%b5%d1%88%d0%bd%d0%b8%d0%b5-%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d1%8b-%d0%b2%d0%bd%d1%83%d1%82%d1%80%d0%b8-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d1%8f" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Внешние пакеты внутри приложения
&lt;/h3>
&lt;p>Пакеты можно добавлять через &lt;code>File&lt;/code> &amp;gt; &lt;code>Swift Packages&lt;/code>. Поиск по &lt;code>url&lt;/code> пакета. Пакет скачается и попробует построить дерево совместимости.&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://agapovone.github.io/swiftpm/dependencies.png" alt="" width=476 height="912" />&lt;/p>
&lt;p>&lt;code>swift package&lt;/code> умеет создавать, собирать и запускать модули. Поставляется вместе с Xcode.&lt;/p>
&lt;p>На CI пакеты резолвятся автоматически при начале сборки, и дополнительных действий делать не нужно.&lt;/p>
&lt;h3 id="модульное-приложение-на-swift-package-manager">
&lt;a href="#%d0%bc%d0%be%d0%b4%d1%83%d0%bb%d1%8c%d0%bd%d0%be%d0%b5-%d0%bf%d1%80%d0%b8%d0%bb%d0%be%d0%b6%d0%b5%d0%bd%d0%b8%d0%b5-%d0%bd%d0%b0-swift-package-manager" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Модульное приложение на Swift Package Manager
&lt;/h3>
&lt;p>&lt;strong>Модули&lt;/strong> выделять стало намного проще. &lt;code>File&lt;/code> &amp;gt; &lt;code>New&lt;/code> &amp;gt; &lt;code>Swift Package&lt;/code>. Добавляя его в репозиторий и проект - появляется понятная структура. Она повторяет файлы внутри, в отличие от обычных проектов. Генерируется папка тестов, автоматом считывается Scheme для билда и тестирования пакета. Зависимости такого модуля можно объявить в Package.swift&lt;/p>
&lt;h2 id="структура-пакетов">
&lt;a href="#%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d0%be%d0%b2" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Структура пакетов
&lt;/h2>
&lt;p>1 репозиторий = коллекция пакетов.&lt;/p>
&lt;p>&lt;strong>Executable&lt;/strong> — cli. Удобно разрабатывать с любыми пакетами, которые тоже поддерживают SwiftPM. &lt;a href="https://github.com/apple/swift-argument-parser">swift-argument-parser&lt;/a> — мощь.&lt;/p>
&lt;p>К сожалению, использовать с SPM SwiftGen/R.swift и любые другие CLI, как это было с Cocoapods - не получится. Валидные решения такие:&lt;/p>
&lt;ul>
&lt;li>положить бинарь с утилитой в код проекта и запускать внутри. Будет не очень удобно обновляться, но все остальные удобства останутся.&lt;/li>
&lt;li>использовать Brewfile/Gemfile для контроля зависимостей. Будет удобно обновлять, но нужно запускать перед билдом brew install/bundle install&lt;/li>
&lt;/ul>
&lt;p>&lt;strong>Library&lt;/strong> — библиотека. Может быть как статической, так и динамической.&lt;/p>
&lt;h2 id="как-удобно-разрабатывать">
&lt;a href="#%d0%ba%d0%b0%d0%ba-%d1%83%d0%b4%d0%be%d0%b1%d0%bd%d0%be-%d1%80%d0%b0%d0%b7%d1%80%d0%b0%d0%b1%d0%b0%d1%82%d1%8b%d0%b2%d0%b0%d1%82%d1%8c" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Как удобно разрабатывать
&lt;/h2>
&lt;h3 id="файловая-структура-пакета">
&lt;a href="#%d1%84%d0%b0%d0%b9%d0%bb%d0%be%d0%b2%d0%b0%d1%8f-%d1%81%d1%82%d1%80%d1%83%d0%ba%d1%82%d1%83%d1%80%d0%b0-%d0%bf%d0%b0%d0%ba%d0%b5%d1%82%d0%b0" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Файловая структура пакета
&lt;/h3>
&lt;p>Внутри репозитория пакета можно собрать такую иерархию файлов:&lt;/p>
&lt;ul>
&lt;li>📁 Project (git repo)
&lt;ul>
&lt;li>📄 Package.swift&lt;/li>
&lt;li>📄 Package.resolved&lt;/li>
&lt;li>📄 README.md&lt;/li>
&lt;li>📁 Sources
&lt;ul>
&lt;li>📁 Helpers&lt;/li>
&lt;li>📁 BigModule&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>📁 Tests
&lt;ul>
&lt;li>📁 BigModuleTests (folder)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>📁 Example
&lt;ul>
&lt;li>Example.xcodeproj&lt;/li>
&lt;li>&amp;hellip; any structure of example project&lt;/li>
&lt;/ul>
&lt;/li>
&lt;li>📄 Packages.xcworkspace&lt;/li>
&lt;li>Packages.playground (еще не пробовал, но должно тоже работать)&lt;/li>
&lt;/ul>
&lt;/li>
&lt;/ul>
&lt;p>xcworkspace файл объединит в себе две группы&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-xml" data-lang="xml">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">&amp;lt;?xml version=&amp;#34;1.0&amp;#34; encoding=&amp;#34;UTF-8&amp;#34;?&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">&amp;lt;Workspace&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#50fa7b">version =&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;1.0&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;lt;FileRef&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#50fa7b">location =&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;group:.&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;lt;/FileRef&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;lt;FileRef&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#50fa7b">location =&lt;/span> &lt;span style="color:#f1fa8c">&amp;#34;group:Example/Example.xcodeproj&amp;#34;&lt;/span>&lt;span style="color:#ff79c6">&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">&amp;lt;/FileRef&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#ff79c6">&amp;lt;/Workspace&amp;gt;&lt;/span>
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>В таком случае xcworkspace повторяет всю структуру папок, при этом имея все схемы для сборки пакетов + схему для сборки Example проекта.&lt;/p>
&lt;p>К сожалению, иногда приходится закрыть-открыть workspace. Обычно это проблемы со схемами или резолвом пакетов.&lt;/p>
&lt;p>&lt;strong>Kontur&lt;/strong>: мы используем такую иерархию в packages-ios.&lt;/p>
&lt;h3 id="packageswift">
&lt;a href="#packageswift" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Package.swift
&lt;/h3>
&lt;p>&lt;code>// swift-tools-version:5.3&lt;/code> - актуальная версия тулзов. Актуальная фича в этой версии - process/copy ресурсов.&lt;/p>
&lt;p>&lt;a href="https://docs.swift.org/package-manager/PackageDescription/index.html">API Reference swift.org&lt;/a> — место, где можно посмотреть документацию к каждому вызову внутри Package.swift&lt;/p>
&lt;p>Про отличие static и dynamic библиотек можно почитать &lt;a href="https://theswiftdev.com/deep-dive-into-swift-frameworks/">в блоге theswiftdev&lt;/a>.&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">33
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">34
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">35
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">36
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">37
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">38
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">39
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">40
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">41
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">42
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">43
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-swift" data-lang="swift">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#6272a4">// swift-tools-version:5.3&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">import&lt;/span> &lt;span style="color:#50fa7b">PackageDescription&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">package&lt;/span> = Package(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;SimplePackage&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> platforms: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .iOS(.v11) &lt;span style="color:#6272a4">// поддерживает только iOS с версии 11&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> products: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .library(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;Helpers&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targets: [&lt;span style="color:#f1fa8c">&amp;#34;Helpers&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .library(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;BigModule&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targets: [&lt;span style="color:#f1fa8c">&amp;#34;BigModule&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .package(url: &lt;span style="color:#f1fa8c">&amp;#34;https://github.com/marmelroy/PhoneNumberKit&amp;#34;&lt;/span>, .upToNextMajor(from: &lt;span style="color:#f1fa8c">&amp;#34;3.3.3&amp;#34;&lt;/span>)),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .package(url: &lt;span style="color:#f1fa8c">&amp;#34;https://github.com/ReactiveX/RxSwift.git&amp;#34;&lt;/span>, from: &lt;span style="color:#f1fa8c">&amp;#34;5.0.0&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .package(name: &lt;span style="color:#f1fa8c">&amp;#34;Firebase&amp;#34;&lt;/span>, url: &lt;span style="color:#f1fa8c">&amp;#34;https://github.com/firebase/firebase-ios-sdk.git&amp;#34;&lt;/span>, from: &lt;span style="color:#f1fa8c">&amp;#34;8.0.0&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> targets: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .target(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;Helpers&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: [&lt;span style="color:#f1fa8c">&amp;#34;PhoneNumberKit&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .target(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;BigModule&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: [
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .product(name: &lt;span style="color:#f1fa8c">&amp;#34;FirebaseRemoteConfig&amp;#34;&lt;/span>, package: &lt;span style="color:#f1fa8c">&amp;#34;Firebase&amp;#34;&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#f1fa8c">&amp;#34;RxSwift&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .product(name: &lt;span style="color:#f1fa8c">&amp;#34;RxCocoa&amp;#34;&lt;/span>, package: &lt;span style="color:#f1fa8c">&amp;#34;RxSwift&amp;#34;&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ],
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> resources: [.process(&lt;span style="color:#f1fa8c">&amp;#34;Resources&amp;#34;&lt;/span>)] &lt;span style="color:#6272a4">// Resources — папка в Sources/BigModule&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> .testTarget(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> name: &lt;span style="color:#f1fa8c">&amp;#34;BigModuleTests&amp;#34;&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> dependencies: [&lt;span style="color:#f1fa8c">&amp;#34;BigModule&amp;#34;&lt;/span>]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ]
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;h3 id="релизы">
&lt;a href="#%d1%80%d0%b5%d0%bb%d0%b8%d0%b7%d1%8b" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Релизы
&lt;/h3>
&lt;p>Релизы легче, чем в cocoapods, так как централизованного хранилища версий необходимости держать нет. Просто добавляем тег версии по &lt;a href="semver.org">semver&lt;/a>, и пакеты готовы к обновлению.&lt;/p>
&lt;p>Если хочется выложить пакет в общий доступ и позволить пользователям искать его в общем списке — посмотри на &lt;a href="https://swiftpackageindex.com">Swift Package Index&lt;/a>.&lt;/p>
&lt;h2 id="как-решать-проблемы">
&lt;a href="#%d0%ba%d0%b0%d0%ba-%d1%80%d0%b5%d1%88%d0%b0%d1%82%d1%8c-%d0%bf%d1%80%d0%be%d0%b1%d0%bb%d0%b5%d0%bc%d1%8b" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Как решать проблемы
&lt;/h2>
&lt;p>Пакеты начинают резолвиться при открытии Xcode проекта. Если удалить пакеты, то они пропадут и билд не будет их видеть. Тогда нужно воспользоваться меню &lt;code>File&lt;/code> &amp;gt; &lt;code>Swift Packages&lt;/code> &amp;gt; &lt;code>Resolve Package Versions&lt;/code>&lt;/p>
&lt;p>&lt;strong>Проблемы кэширования&lt;/strong> решаются удалением кэша 😀 Ссылка на него лежит в &lt;code>~/.swiftpm/cache&lt;/code>. Оригинал лежит в &lt;code>~/Library/Caches/org.swift.swiftpm&lt;/code>. Рядом с кэшем Cocoapods.&lt;/p>
&lt;p>&lt;strong>Проблема совместимости&lt;/strong> с тем, что модули не подходят друг другу решается вручную. Как, например, если мы поставим &lt;a href="https://github.com/ReactiveX/RxSwift">RxSwift&lt;/a> 6.0.0+ и последнюю версию &lt;a href="https://github.com/Moya/Moya/releases/tag/14.0.1">Moya&lt;/a>, которая работает с RxSwift 5.0.0+. Тогда нужно явно указать, что версия RxSwift необходима старая. Автоматически это не решится, но лог пишется понятный.&lt;/p>
&lt;p>&lt;img loading="lazy" src="https://agapovone.github.io/swiftpm/versions.png" alt="" width=616 height="158" />&lt;/p>
&lt;p>&lt;strong>Проблема с разными версиями одного пакета&lt;/strong>&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-text" data-lang="text">&lt;span style="display:flex;">&lt;span>Dependencies could not be resolved because root depends on &amp;#39;Moya&amp;#39; 14.0.1..&amp;lt;15.0.0 and root depends on &amp;#39;Quick&amp;#39; 4.0.0..&amp;lt;5.0.0.
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>&amp;#39;Moya&amp;#39; &amp;gt;= 14.0.1 practically depends on &amp;#39;Quick&amp;#39; 2.0.0..&amp;lt;3.0.0 because &amp;#39;Moya&amp;#39; 14.0.1 depends on &amp;#39;Quick&amp;#39; 2.0.0..&amp;lt;3.0.0 and no versions of &amp;#39;Moya&amp;#39; match the requirement 14.0.2..&amp;lt;15.0.0.
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Не вижу возможного решения в текущей версии, так как Package.swift нельзя выделить отдельно. Думал о том, чтобы Quick с версией другой имел название библиотеки вроде &lt;code>Quick-4&lt;/code>, и тогда бы не должна была ломаться связка. Но как такое решить с текущей интеграцией Xcode - не понятно.&lt;/p>
&lt;p>Валидные решения на сейчас:&lt;/p>
&lt;ul>
&lt;li>выпилить Quick из тестов :)&lt;/li>
&lt;li>обновить Moya в отдельном репозитории. Версия 15 с поддержкой RxSwift давно готова. Но её не выпускают непонятно почему. Скорее всего, проблема в документации.&lt;/li>
&lt;/ul>
&lt;h2 id="полезные-ссылки">
&lt;a href="#%d0%bf%d0%be%d0%bb%d0%b5%d0%b7%d0%bd%d1%8b%d0%b5-%d1%81%d1%81%d1%8b%d0%bb%d0%ba%d0%b8" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Полезные ссылки
&lt;/h2>
&lt;p>&lt;a href="https://swift.org/package-manager/">Обзор по SwiftPM от Apple&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://docs.swift.org/package-manager/">Документация по API SwiftPM в Package.swift файле&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://developer.apple.com/videos/developer-tools/swift?q=package">Видео WWDC про Swift Packages&lt;/a>&lt;/p>
&lt;p>&lt;a href="https://swiftpackageindex.com">Публичный список доступных пакетов в SwiftPM&lt;/a>&lt;/p>
&lt;h2 id="upd-122021">
&lt;a href="#upd-122021" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
UPD: 12.2021
&lt;/h2>
&lt;p>Всё ещё очень долго происходит загрузка пакетов при открытии проекта в Xcode. Если подряд открыть проект несколько раз — то и загрузка будет происходить каждый из этих раз. Это может занимать очень много времени. В Cocoapods же можно было не обновлять и не подтягивать каждый раз пакеты.&lt;/p></description></item><item><title>Modern Collection Views</title><link>https://agapovone.github.io/articles/moderncollection/</link><pubDate>Wed, 21 Jul 2021 10:49:14 +0500</pubDate><guid>https://agapovone.github.io/articles/moderncollection/</guid><description>
&lt;p>&lt;a href="https://developer.apple.com/documentation/uikit/views_and_controls/collection_views/implementing_modern_collection_views">source от Apple&lt;/a>&lt;/p>
&lt;p>iOS 14+ для реализации всех фич.
В iOS 13 ввели diffable data source.
В iOS 14 появился compositional layout, реализующий все возможности таблиц и улучшающий коллекции.&lt;/p>
&lt;h2 id="почему-не-таблица">
&lt;a href="#%d0%bf%d0%be%d1%87%d0%b5%d0%bc%d1%83-%d0%bd%d0%b5-%d1%82%d0%b0%d0%b1%d0%bb%d0%b8%d1%86%d0%b0" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Почему не таблица?
&lt;/h2>
&lt;ul>
&lt;li>Горизонтальный скролл&lt;/li>
&lt;li>Удобно конфигурировать state. highlighted/selected/disabled&lt;/li>
&lt;/ul>
&lt;h2 id="что-позволяет-делать">
&lt;a href="#%d1%87%d1%82%d0%be-%d0%bf%d0%be%d0%b7%d0%b2%d0%be%d0%bb%d1%8f%d0%b5%d1%82-%d0%b4%d0%b5%d0%bb%d0%b0%d1%82%d1%8c" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Что позволяет делать?
&lt;/h2>
&lt;ul>
&lt;li>Collapsible/Expandable Sections&lt;/li>
&lt;li>Секции с разным layout. inset grouped, sidebar (iPad-related), horizontal scroll, columns.&lt;/li>
&lt;/ul>
&lt;h2 id="главные-фичи">
&lt;a href="#%d0%b3%d0%bb%d0%b0%d0%b2%d0%bd%d1%8b%d0%b5-%d1%84%d0%b8%d1%87%d0%b8" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Главные фичи
&lt;/h2>
&lt;h3 id="data-source">
&lt;a href="#data-source" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Data source
&lt;/h3>
&lt;p>Data-driven работает через… RxDataSources? DifferenceKit? Native!
Собираем &lt;code>NSDiffableDataSourceSnapshot&lt;/code>, применяем.&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">4
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-swift" data-lang="swift">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">var&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">snapshot&lt;/span> = NSDiffableDataSourceSnapshot&amp;lt;Section, &lt;span style="color:#8be9fd;font-style:italic">Int&lt;/span>&amp;gt;()
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>snapshot.appendSections([.main])
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>snapshot.appendItems(&lt;span style="color:#8be9fd;font-style:italic">Array&lt;/span>(&lt;span style="color:#bd93f9">0.&lt;/span>.&amp;lt;&lt;span style="color:#bd93f9">94&lt;/span>))
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dataSource.apply(snapshot, animatingDifferences: &lt;span style="color:#ff79c6">false&lt;/span>)
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Data source — конфигурация с cellRegistration&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-swift" data-lang="swift">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">cellRegistration&lt;/span> = UICollectionView.CellRegistration&amp;lt;TextCell, &lt;span style="color:#8be9fd;font-style:italic">Int&lt;/span>&amp;gt; { (cell, indexPath, identifier) &lt;span style="color:#ff79c6">in&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6272a4">// Populate the cell with our item description.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> cell.label.text = &lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>&lt;span style="color:#f1fa8c">\(&lt;/span>identifier&lt;span style="color:#f1fa8c">)&lt;/span>&lt;span style="color:#f1fa8c">&amp;#34;&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>dataSource = UICollectionViewDiffableDataSource&amp;lt;Section, &lt;span style="color:#8be9fd;font-style:italic">Int&lt;/span>&amp;gt;(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collectionView: collectionView
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>) { (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> collectionView: UICollectionView,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> indexPath: IndexPath,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> identifier: &lt;span style="color:#8be9fd;font-style:italic">Int&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>) -&amp;gt; UICollectionViewCell? &lt;span style="color:#ff79c6">in&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6272a4">// Return the cell.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">return&lt;/span> collectionView.dequeueConfiguredReusableCell(using: cellRegistration, &lt;span style="color:#ff79c6">for&lt;/span>: indexPath, item: identifier)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;p>Есть одна проблемка с native data source. Он постоянно будет удалять и затем добавлять новую ячейку, не делая reload данных на месте (&lt;a href="https://github.com/ekazaev/ChatLayout#about-uicollectionviewdiffabledatasource">тут говорят&lt;/a>).
&lt;a href="https://github.com/ra1028/DifferenceKit">DifferenceKit&lt;/a> позволяет это делать, поддерживает перезагрузку. Бенчмарки этой либы говорят, что она самая быстрая среди всех похожих либ. Хорошая либа
Но для использования с новым API cellRegistration нужно использовать native Data source&lt;/p>
&lt;h3 id="layout-для-сложных-экранов">
&lt;a href="#layout-%d0%b4%d0%bb%d1%8f-%d1%81%d0%bb%d0%be%d0%b6%d0%bd%d1%8b%d1%85-%d1%8d%d0%ba%d1%80%d0%b0%d0%bd%d0%be%d0%b2" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Layout для сложных экранов
&lt;/h3>
&lt;p>&lt;img loading="lazy" src="https://agapovone.github.io/collections/1.png" alt="" width=1170 height="2532" />
&lt;code>DistinctSectionsViewController&lt;/code> как пример разных layout для разных секций.&lt;/p>
&lt;div class="highlight">&lt;div style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">
&lt;table style="border-spacing:0;padding:0;margin:0;border:0;">&lt;tr>&lt;td style="vertical-align:top;padding:0;margin:0;border:0;">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 1
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 2
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 3
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 4
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 5
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 6
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 7
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 8
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f"> 9
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">10
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">11
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">12
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">13
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">14
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">15
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">16
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">17
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">18
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">19
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">20
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">21
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">22
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">23
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">24
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">25
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">26
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">27
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">28
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">29
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">30
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">31
&lt;/span>&lt;span style="white-space:pre;-webkit-user-select:none;user-select:none;margin-right:0.4em;padding:0 0.4em 0 0.4em;color:#7f7f7f">32
&lt;/span>&lt;/code>&lt;/pre>&lt;/td>
&lt;td style="vertical-align:top;padding:0;margin:0;border:0;;width:100%">
&lt;pre tabindex="0" style="color:#f8f8f2;background-color:#282a36;-moz-tab-size:2;-o-tab-size:2;tab-size:2;">&lt;code class="language-swift" data-lang="swift">&lt;span style="display:flex;">&lt;span>&lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">layout&lt;/span> = UICollectionViewCompositionalLayout { (
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> sectionIndex: &lt;span style="color:#8be9fd;font-style:italic">Int&lt;/span>,
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> layoutEnvironment: NSCollectionLayoutEnvironment
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> ) -&amp;gt; NSCollectionLayoutSection? &lt;span style="color:#ff79c6">in&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">guard&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">sectionLayoutKind&lt;/span> = SectionLayoutKind(rawValue: sectionIndex) &lt;span style="color:#ff79c6">else&lt;/span> { &lt;span style="color:#ff79c6">return&lt;/span> &lt;span style="color:#ff79c6">nil&lt;/span> }
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">columns&lt;/span> = sectionLayoutKind.columnCount
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6272a4">// The group auto-calculates the actual item width to make&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6272a4">// the requested number of columns fit, so this widthDimension is ignored.&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">itemSize&lt;/span> = NSCollectionLayoutSize(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> widthDimension: .fractionalWidth(&lt;span style="color:#bd93f9">1.0&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heightDimension: .fractionalHeight(&lt;span style="color:#bd93f9">1.0&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">item&lt;/span> = NSCollectionLayoutItem(layoutSize: itemSize)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> item.contentInsets = NSDirectionalEdgeInsets(top: &lt;span style="color:#bd93f9">2&lt;/span>, leading: &lt;span style="color:#bd93f9">2&lt;/span>, bottom: &lt;span style="color:#bd93f9">2&lt;/span>, trailing: &lt;span style="color:#bd93f9">2&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">groupHeight&lt;/span> = columns == &lt;span style="color:#bd93f9">1&lt;/span> ?
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NSCollectionLayoutDimension.absolute(&lt;span style="color:#bd93f9">44&lt;/span>) :
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> NSCollectionLayoutDimension.fractionalWidth(&lt;span style="color:#bd93f9">0.2&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#6272a4">// Element height takes 20% of section width&lt;/span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">groupSize&lt;/span> = NSCollectionLayoutSize(
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> widthDimension: .fractionalWidth(&lt;span style="color:#bd93f9">1.0&lt;/span>),
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> heightDimension: groupHeight
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> )
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">group&lt;/span> = NSCollectionLayoutGroup.horizontal(layoutSize: groupSize, subitem: item, count: columns)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#8be9fd;font-style:italic">let&lt;/span> &lt;span style="color:#8be9fd;font-style:italic">section&lt;/span> = NSCollectionLayoutSection(group: group)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> section.contentInsets = NSDirectionalEdgeInsets(top: &lt;span style="color:#bd93f9">20&lt;/span>, leading: &lt;span style="color:#bd93f9">20&lt;/span>, bottom: &lt;span style="color:#bd93f9">20&lt;/span>, trailing: &lt;span style="color:#bd93f9">20&lt;/span>)
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> section.contentInsetsReference = .readableContent
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span> &lt;span style="color:#ff79c6">return&lt;/span> section
&lt;/span>&lt;/span>&lt;span style="display:flex;">&lt;span>}
&lt;/span>&lt;/span>&lt;/code>&lt;/pre>&lt;/td>&lt;/tr>&lt;/table>
&lt;/div>
&lt;/div>&lt;ul>
&lt;li>&lt;strong>Layout Environment&lt;/strong> с размерами и трейтами позволит менять layout. Например, в несколько колонок на айпад. Пример — &lt;code>AdaptiveSectionsViewController&lt;/code>&lt;/li>
&lt;li>&lt;strong>Отношение к отступам&lt;/strong> от родных layout margins/readable guide. Настройка у &lt;code>NSCollectionLayoutSection&lt;/code>&lt;/li>
&lt;li>&lt;strong>Поведение скролла&lt;/strong> можно контролировать у секции. Например, с остановкой у leading границы элемента. &lt;code>OrthogonalScrollBehaviorViewController&lt;/code>&lt;/li>
&lt;/ul>
&lt;h3 id="cell-configuration">
&lt;a href="#cell-configuration" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Cell Configuration
&lt;/h3>
&lt;p>У &lt;a href="https://developer.apple.com/documentation/uikit/uicollectionviewcell">UICollectionViewCell&lt;/a> можно по новой управлять свойствами.
&lt;a href="https://developer.apple.com/documentation/uikit/uicollectionviewcell/3600949-contentconfiguration">contentConfiguration&lt;/a> настраивает контент (текст, accessory&lt;/p>
&lt;p>List Cell для Expandable/Collapsible sections. Позволяет изменять indentation (отступ слева)
&lt;a href="https://developer.apple.com/documentation/uikit/uicollectionviewlistcell">UICollectionViewListCell&lt;/a>&lt;/p>
&lt;h2 id="хорошие-новости-для-дизайнеров">
&lt;a href="#%d1%85%d0%be%d1%80%d0%be%d1%88%d0%b8%d0%b5-%d0%bd%d0%be%d0%b2%d0%be%d1%81%d1%82%d0%b8-%d0%b4%d0%bb%d1%8f-%d0%b4%d0%b8%d0%b7%d0%b0%d0%b9%d0%bd%d0%b5%d1%80%d0%be%d0%b2" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Хорошие новости для дизайнеров
&lt;/h2>
&lt;h3 id="тень-для-всей-секции-insetgrouped-элементов-или-рамка">
&lt;a href="#%d1%82%d0%b5%d0%bd%d1%8c-%d0%b4%d0%bb%d1%8f-%d0%b2%d1%81%d0%b5%d0%b9-%d1%81%d0%b5%d0%ba%d1%86%d0%b8%d0%b8-insetgrouped-%d1%8d%d0%bb%d0%b5%d0%bc%d0%b5%d0%bd%d1%82%d0%be%d0%b2-%d0%b8%d0%bb%d0%b8-%d1%80%d0%b0%d0%bc%d0%ba%d0%b0" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Тень для всей секции insetGrouped элементов. Или рамка.
&lt;/h3>
&lt;p>Изи!
&lt;img loading="lazy" src="https://agapovone.github.io/collections/2.png" alt="" width=1170 height="2532" />
&lt;code>SectionBackgroundDecorationView&lt;/code> в примере.&lt;/p>
&lt;h3 id="прилипающие-хедерыфутеры-секций">
&lt;a href="#%d0%bf%d1%80%d0%b8%d0%bb%d0%b8%d0%bf%d0%b0%d1%8e%d1%89%d0%b8%d0%b5-%d1%85%d0%b5%d0%b4%d0%b5%d1%80%d1%8b%d1%84%d1%83%d1%82%d0%b5%d1%80%d1%8b-%d1%81%d0%b5%d0%ba%d1%86%d0%b8%d0%b9" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Прилипающие хедеры/футеры секций
&lt;/h3>
&lt;p>Изи!
&lt;img loading="lazy" src="https://agapovone.github.io/collections/3.png" alt="" width=1170 height="2532" />
&lt;code>PinnedSectionHeaderFooterViewController&lt;/code> в примере.&lt;/p>
&lt;h3 id="пагинация-при-скролле">
&lt;a href="#%d0%bf%d0%b0%d0%b3%d0%b8%d0%bd%d0%b0%d1%86%d0%b8%d1%8f-%d0%bf%d1%80%d0%b8-%d1%81%d0%ba%d1%80%d0%be%d0%bb%d0%bb%d0%b5" class="anchor">
&lt;svg class="icon" aria-hidden="true" focusable="false" height="16" version="1.1" viewBox="0 0 16 16" width="16">
&lt;path fill-rule="evenodd"
d="M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z">
&lt;/path>
&lt;/svg>
&lt;/a>
Пагинация при скролле
&lt;/h3>
&lt;p>&lt;img loading="lazy" src="https://agapovone.github.io/collections/4.png" alt="" width=1668 height="2388" />
&lt;code>OrthogonalScrollBehaviorViewController&lt;/code> в примере&lt;/p></description></item></channel></rss>