Начну с того, что в поисках информации по SwiftUI, я не нашел ничего лучше, чем туториал от корпорации Apple - вот он. Если плохо с английским, браузер Chrome в помощь, справляется на ура. Достаточно повторить весь предоставленный материал и 99% вопросов отпадут сами собой. Материал рассчитан на людей, которые имеют какой-либо незначительный опыт разработки и хотят познакомиться со SwiftUI. Фреймворк уже достаточно мощный и к Storyboard я более не вернусь. Конечно часто приходится использовать UIKit, но думаю с течением времени эта необходимость сойдет на нет. Производительность при правильном проектировании, просто поражает воображение, скорость разработки так же удивляет. Доказывать никому и ничего не буду, как говорится, сколько людей, столько и мнений, пожалуй начну. Шпаргалка по SwiftUI Некоторые нюансы работы SwiftUI PageView на SwiftUI WebImage на SwiftUI (AsyncImage) В общем, постараюсь поделится своими значимыми наблюдениями, которые я выполнил, в момент разработки клиент-серверного приложения. Суть приложения - обои на рабочий стол iPhone. Кто хочет пощупать, оно есть в AppStore  - сильвупле. Приложение умеет сохранять картинки (Data) в .cache, сохранять картинки в фотопленку, показывать картинки, работать с сетью (запросы списков, авторизация и т.д.), грубо говоря ничего сложного. Об этом и как это реализовано, в том числе и будет идти речь. @State - своеобразная обертка для свойств, их можно передавать и они реактивно связаны с представлением. Если попробовать объяснить простым языком, в представленном выше листинге, можно наблюдать свойство структуры представления - text, по сути переменная, которая одновременно является и String т.е. можно использовать её как обычно, плюс находится в обертке (Binding<String>). Некоторые представления требуют именно Binding<>, как например TextField() (почему так происходит, поймете чуть ниже), указывать такие свойства необходимо через $ (это не отсылка к php). Соответственно, указывая это свойство в любом представлении (в SwiftUI, каждый элемент является представлением), например в Text(), мы получаем реактивную связку и изменяя TextField(), мы сразу увидим изменения в Text(). Просто великолепная возможность, кроме этого мы можем менять значения таких свойств из методов без дополнительных действий, к примеру как вам известно, в структурах, для изменения обычных свойств методами, необходимо использовать mutating, а вот свойств в обертке Binding<> как вы уже возможно догадались, это не касается, они поддаются прямому изменению. Свойства Binding<> не могут быть вычисляемыми и им нельзя назначать сеттеры, но есть выход - для всех действий надлежит использовать модификатор onChange @Binding - получаемое структурой извне, свойство, с оберткой @State Выше можно наблюдать, как текст в MyView связан с полем ввода в MySecondView. Теперь я думаю стало понятно, почему каждый элемент вашего представления является представлением, а так же иногда требует именно свойства в Binding<> обертке. Это очень изящное и простое решение, которое позволят связывать различные структуры между собой, разбивать сложные представления на части. @StateObject - обертка для объекта, которая по сути является аналогичной @State, часто бывает удобно для структуризации, с той лишь разницей, что нельзя передать объект @Binding целиком (для этого существует другое решение), а только его свойства @ObservedObject - как раз то, что помогает принять @StateObject из другого представления (никогда не следует использовать его для инициализации экземпляра). @EnvironmentObject - я бы назвал это точкой входа в приложение, если вы знакомы с различными фрейворками, работой с Docker и т.д., вам хорошо знаком файл .env (переменные окружения), так вот @EnvironmentObject по своей сути и есть объект окружения, но со своим методами и содержанием, который может так же содержать объекты. Объявляется в начале приложения и передается посредством модификатора главному представлению. Это позволяет использовать его во всех представлениях, просто указав его наличие. Что с одной стороны удобно, но может вызвать ряд неудобств т.к. @EnvironmentObject (среда) передается после инициализации структуры, следовательно использовать init() у Вас не получится (будет ошибка). .onAppear модификатор - иногда необходимо сделать какие-либо действия в момент инициализации представления и очевидным решением будет внедрение функционала в init() но как мы выяснили ранее, использование @EnvironmentObject блокирует эту возможность, onAppear как раз таки решает эту проблему Код заключенный в модификатор будет выполнен в момент появления представления на экране, что бывает более удобно нежели работа с инициализатором, который выполняется в момент инициализации и в целом может здорово повысить производительность. Конечно следует учитывать отсутствие возможности задавать свои имена при передачи аргументов и необходимость гарантировать появление. .onDisappear модификатор - противоположность onAppear, срабатывает когда представление покидает экран. Модификаторы - средство стилизации/изменения представлений, можно писать свои посредством расширения View, рассказывать особо нечего, если вам тяжело понять как они работают, посмотрите на следующий листинг собственно, наглядный пример, который позволяет понять принцип их работы. TableView  Первый кровопийца в моем списке пожирателей нервной системы. Самый простой и популярный способ представить полноэкранный просмотр чего либо, достаточно применить один модификатор. Он манит своей легкостью в использовании, забейте в Google "PageView SwiftUI" и найдете десятки вариаций использования TabView, но по факту подобная реализация от лукавого. Если мы добавим всего пару сотен пустых представлений с цветом, все начинает лагать даже на iPhone 13 Pro Когда речь идет о тысячах, речи о производительности и быть не может. С помощью в туториале от Apple, который упоминался выше, я нашел для себя вариант решения проблемы, об этом ниже, когда буду разбирать PageView. DispatchQueue Все изменения в ObservableObject объектах, необходимо производить в главном потоке т.к. SwiftUI подобен UIKit, где вносить изменения в представления разрешено только в главном потоке. У Вас конечно получится и вне, жесткого ограничения нет, но к каким результатам это приведет неизвестно. Делается очень просто. Combine Сейчас в нем не предусмотрена реализация URLSession.shared.downloadTask, поэтому не нужно искать легких путей и использовать для загрузки файлов предусмотренный dataTask, согласно документации Apple его надлежит применять исключительно для загрузки небольшого количества данных. Файлы к этому конечно же не относятся. Я понимаю, что он манит своей простотой, но тем не менее, это крайне негативно сказывается на производительности. Пример реализации будет ниже в WebImage. Apple позволят использовать компоненты других фреймворков в SwiftUI и решение вполне себе очевидно. Использовать UIViewControllerRepresentable Я думаю простейшая реализация не требует каких либо комментариев, как вы можете заметить, мы без проблем можем использовать @StateObject, @EnvironmentObject и прочие плюшки SwiftUI, пример использования Пример примитивный, но думаю раскрывает возможности, можете скопировать код в пустой проект и побаловаться, нет намека на лаги даже на винтажном iPhone 6s. Никто не мешает передавать вам готовый массив или получать его где либо еще, передавать отдельный счетчик страниц и т.д. В приложении я использовал @StateObject т.к. в моей архитектуре это удобно. В листинге просто пример, не стоит использовать его как руководство. SwiftUI не имеет встроенных (не имел до выхода iOS 15) средств для загрузки изображений из интернета и по сей день не имеет средств для их загрузки с последующим кэшированием/сохранением. Ниже я приведу пример кода, который покажет на простом примере, как качать изображения из сети, а затем их использовать. ПОЖАЛУЙСТА не используйте это в своих проектах, пример очень сильно упрощен для наглядности и выполнен не правильно, выполнен в рамках структуры (классы - ссылочный тип, структуры копируемый) и негативно влияет на ОЗУ, не выносит операции в поток утилит и т.д. создан исключительно для ознакомления и вашей возможности побаловаться. Поэтому если решение Вам понравится, разберите каждую строчку кода и выполните реализацию самостоятельно  и правильно. Для начала создадим структуру  Нам нужно будет передавать в нее ссылку  Так как мы планируем хранить изображение в памяти нам нужен метод, который переделает ссылку в имя файла. Создадим метод проверки наличия файла/директории Создадим промежуточное вычисляемое свойство, содержащее путь к папке с кэшем Затем нам нужен метод возвращающий путь к папке с кэшем В методе мы находим папку .cache, затем создаем папку image и возвращаем путь к ней в виде URL Далее, так как мы все таки делаем пример, создадим страшную вещь. В общем если что-либо пойдет не так, приложение крашнется) Не делайте так. Мы получаем удобный доступ к опциональному значению через явное извлечении. Можно конечно просто «!» ставить и избежать этого обходного пути, но разве это интересно? Продолжим. Заводим флаг загрузки. Реализуем метод извлечения изображения из Data Создание свойства с изображением и для прогресса загрузки. И теперь когда у нас есть, все что необходимо - реализуем загрузку изображения. Реализуем в body отображение В совокупности должен получится вот такой код (c вашей ссылкой на изображение) Но повторюсь, подобные вещи, таким способом не реализуются. Сюда нужно сделать загрузку в NSCache и извлечение из него (об этом информации достаточно), разбить все это на сервисы и т.д. реализовать множество различных проверок. Моя же задача была предоставить пример. Собственно по теме текущего материала я предоставил все, что хотел. По мере возможности постараюсь выложить изолированный сервис контейнер для загрузки изображений, вне контекста, но это уже другая история. Если есть что добавить, или исправить — добро пожаловать в комментарии. Разработчик