Swift ui что это
Перейти к содержимому

Swift ui что это

  • автор:

Что лучше: UIKit или SwiftUI?

Hello, World! Меня зовут Денис. Я IOS разработчик, пишу приложения для App Store. Хочу поделиться своим небольшим опытом на UIKit и SwiftUI.

Первый запуск

На WWDC19 Apple предоставила декларативный фреймворк SwiftUI. Новый фреймворк позволяет уменьшать время на написание UI-составляющей своих приложений.

  • UIKit
  • SwiftUI
  • Отличия между фреймворками
  • Возможно использовать UIKit в SwiftUI?
  • Производительность

Что такое фреймворк UIKit?

UIKit — это фреймворк, позволяющий создавать пользовательские интерфейсы (UI), которые могут обрабатывать события касания и входные данные, управляя взаимодействиями между пользователем, системой и вашим приложением. UIKit разработан и выпущен на основе языка Objective-C.

UIKit можно создавать несколькими способами

  • Использовать конструктор интерфейса. Interface Builder интегрирован в Xcode и позволяет редактировать .storyboard.xib
  • Подход, ориентированный на код, при котором представления и ограничения компоновки определяются в Swift

Плюсы/минусы

+ Привычен и стабилен

+ Свобода действий над UI элементами

— Сложные UI элементы

Что такое фреймворк SwiftUI?

SwiftUI фреймворк, который позволяет вам проектировать и разрабатывать пользовательские интерфейсы декларативно, с меньшим количеством кода. Был впервые выпущен в 2019 году с версией 13 iOS SDK.

  • Xcode отображает визуальный редактор с любым файлом, содержащим представление SwiftUI, отображая живое представление создаваемого вами представления.

Плюсы/минусы

+ Прост в освоении

+ Можно смешивать с UIKit через UIHostingController

+ SwiftUI больше не нуждается в Interface Builder, был заменен на Canvas

+ HStack(элементы расположены горизонтально), VStack(элементы расположены вертикально), ZStack(элементы расположены друг над другом) — StackView

+ Абсолютная свобода и гибкость

— Еще молодой фреймворк

— Он поддерживает только iOS 13 и Xcode 11

Отличия между фреймворками

1) Чтобы код работал, вам просто нужно описать переменную body ( var body: some View<> ) , к которой вы должны вернуть представление. Тело — ваш контейнер, в который вы добавляете все остальные вложенные представления. В теле вы должны возвращать только один элемент: текст, изображение, кнопку. — все, что поддерживает View . В другом случае компилятор выдаст ошибку

2) Основные вещи, такие как UIScrollView и UITableView также доступны, но теперь они называются ScrollView и ListView

3) Создание пользовательского интерфейса программно (без раскадровки) в UIKit значительно сложнее по сравнению с SwiftUI. UIKit известен как императивный фреймворк, который просто означает, что вы указываете, как что-то сделать

4) SwiftUI является декларативной структурой. Вы объявляете код, а на canvas происходит ваши задумки (как я называю «прямой эфир»)

Возможно использовать UIKit в SwiftUI?

UIViewRepresentable — это протокол, предоставляемый фреймворком SwiftUI. Используя этот протокол, можно обернуть экземпляр представления UIKit, чтобы его можно было отображать с помощью SwiftUI. Пример:

Производительность

С точки зрения времени разработки SwiftUI обычно работает лучше, чем UIKit. Связано с тем, что иерархия представлений находится в структурах типа значений, хранящихся в стеке, что означает отсутствие дорогостоящего выделения памяти. Означает более высокую производительность в некоторых ситуациях

Итог

Подводя итог, SwiftUI использует совершенно иной подход к написанию кода, чем UIKit. Выбор за вами. Факт: SwiftUI понятен, легко читается и удобен в использовании. В будущем Apple начнет отказываться от UIKit.

Новое поколение iOS-разработчиков все чаще начинают свой путь именно со SwiftUI, а бывшие дизайнеры, благодаря данной технологии Apple, становятся еще и разработчиками.

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

P.S. На рынке РФ пока мало компаний, которые перешли с UIKit на SUI.

Поток данных SwiftUI с примерами

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

9 месяцев назад

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

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

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

@Binding

Обертка свойства @Binding позволяет создать двустороннюю связь между свойством, хранящим данные, и представлением, которое отображает и изменяет эти данные. Привязка (Binding) соединяет свойство с источником истины, хранящимся в другом месте, вместо того, чтобы хранить данные напрямую. Например, вы можете использовать привязку для создания двусторонней связи между Toggle и свойством Boolean объекта State.

struct ToggleView: View < @Binding var isOn: Bool var body: some View < Toggle("Switch", isOn: $isOn) >> struct ContentView: View < @State private var isOn = false var body: some View < ToggleView(isOn: $isOn) >>

В этом примере ContentView имеет свойство @State, которое хранит булево значение для переключателя. Он передает привязку этого свойства в ToggleView, который использует его для отображения и обновления переключения. Префикс $ перед именем свойства дает доступ к его прогнозируемому значению, которое для свойства @State является привязкой к значению.

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

@StateObject

Обертка свойства @StateObject позволяет вам создавать и сохранять наблюдаемый объект, принадлежащий представлению. Вы используете эту обертку свойства, когда хотите, чтобы ваше представление было владельцем объекта и отвечало за его создание и уничтожение.

Наблюдаемый объект — это пользовательский класс, который соответствует протоколу ObservableObject и может использоваться для хранения данных и логики приложения. Он также может использовать обертку свойства @Published, чтобы пометить некоторые из своих свойств как опубликованные, что означает, что они будут вызывать обновления представления при каждом изменении.

class Counter: ObservableObject < @Published var value = 0 func increment() < value += 1 >> struct CounterView: View < @StateObject var counter = Counter() var body: some View < VStack < Text("Count: \(counter.value)") Button("Increment") < counter.increment() >> > >

В этом примере CounterView создает и владеет экземпляром Counter, который является наблюдаемым объектом, хранящим и обновляющим значение. Представление использует @StateObject для сохранения объекта во время обновления представления и предотвращения многократной инициализации. Представление также наблюдает за изменениями опубликованного значения и отображает его в текстовом представлении.

Вы должны использовать @StateObject только в представлениях, которые отвечают за создание наблюдаемого объекта. Если вам нужно передать существующий наблюдаемый объект в другое представление, вместо него следует использовать @ObservedObject или @EnvironmentObject.

@Environment

Обертка свойства @Environment позволяет прочитать значение, хранящееся в окружении представления. Окружение — это набор пар ключ-значение, которые SwiftUI использует для передачи информации вниз по иерархии представления. С помощью этой обертки свойств можно получить доступ к различным системным настройкам и предпочтениям.

Например, вы можете использовать @Environment для чтения текущей цветовой схемы вашего приложения:

struct ContentView: View < @Environment(\\.colorScheme) var colorScheme var body: some View < Text("Color scheme: \(colorScheme == .dark ? "Dark" : "Light")") >>

В этом примере ContentView использует @Environment для чтения значения colorScheme из среды и отображает его в текстовом представлении. Если пользователь изменит настройки внешнего вида системы, представление автоматически обновится, чтобы отразить новое значение.

Вы можете использовать @Environment для чтения любого из предопределенных ключей в структуре EnvironmentValues, таких как horizontalSizeClass, managedObjectContext, locale и других. Вы также можете определить собственные пользовательские значения среды, создав пользовательский ключ, соответствующий протоколу EnvironmentKey, и расширив структуру EnvironmentValues, чтобы добавить вычисляемое свойство для вашего ключа.

Вы не можете использовать @Environment для записи или изменения значения среды. Для этого нужно использовать модификатор environment(_ 🙂 в представлении и передать ключ и новое значение. Это установит или переопределит значение окружения для текущего представления и всех его вложенных представлений.

@Published

Обертка свойства @Published позволяет пометить свойство наблюдаемого объекта как опубликованное, что означает, что он будет уведомлять своих наблюдателей при каждом изменении значения свойства. Вы используете эту обертку свойства внутри пользовательского класса, который соответствует протоколу ObservableObject.

class User: ObservableObject < @Published var name: String @Published var age: Int init(name: String, age: Int) < self.name = name self.age = age >>

В этом примере User — это наблюдаемый объект, который имеет два опубликованных свойства: имя и возраст. При изменении одного из этих свойств SwiftUI автоматически обновляет все представления, которые зависят от них.

Вы можете использовать @Published для создания простых моделей данных, которые могут быть совместно использованы в вашем приложении с помощью оберток свойств, таких как @StateObject, @ObservedObject или @EnvironmentObject. Вы также можете использовать его для создания кастомных паблишеров, которые можно использовать с фреймворком Combine.

@State

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

Свойство State — это одно значение или коллекция значений, которые соответствуют протоколу Value, что означает, что они являются структурами, имеющими семантику Value. Например, вы можете использовать свойство state для хранения булевского флага, целочисленного счетчика или массива строк.

struct CounterView: View < @State private var count = 0 var body: some View < VStack < Text("Count: \(count)") Button("Increment") < count += 1 >> > >

В этом примере у CounterView есть свойство state, которое хранит целочисленное значение для счета. Представление отображает счетчик в текстовом представлении и обновляет его, когда пользователь нажимает кнопку. Свойство state помечено как private, потому что оно используется только этим представлением и не должно быть доступно другим представлениям.

Вы должны использовать @State только для простых данных, локальных для одного представления. Если вам необходимо обмениваться данными между несколькими представлениями или хранить сложные данные с семантикой ссылок, вместо этого следует использовать другие обертки свойств, например @StateObject, @ObservedObject или @EnvironmentObject.

@EnvironmentObject

Обертка @EnvironmentObject позволяет вам создать и наблюдать observable объект, который является общим для всей иерархии представлений. Вы используете эту обертку свойства, когда хотите, чтобы ваши представления имели доступ к общему источнику данных без необходимости передавать его в явном виде через инициализаторы или биндинги.

Объект среды — это экземпляр пользовательского класса, который соответствует протоколу ObservableObject и может использоваться для хранения данных и логики приложения. Он также может использовать обертку свойств @Published, чтобы пометить некоторые из своих свойств как опубликованные, что означает, что они будут вызывать обновления представления при каждом изменении.

class UserSettings: ObservableObject < @Published var username = "Guest" >struct SettingsView: View < @EnvironmentObject var settings: UserSettings var body: some View < TextField("Username", text: $settings.username) >> struct ProfileView: View < @EnvironmentObject var settings: UserSettings var body: some View < Text("Hello, \(settings.username)!") >> struct ContentView: View < @StateObject var settings = UserSettings() var body: some View < TabView < SettingsView() .tabItem < Image(systemName: "gear") Text("Settings") >ProfileView() .tabItem < Image(systemName: "person") Text("Profile") >> .environmentObject(settings) > >

В этом примере ContentView создает и владеет экземпляром UserSettings, который является наблюдаемым объектом, хранящим имя пользователя. Представление использует @StateObject для сохранения объекта во время обновления представления и предотвращения многократной инициализации. Представление также использует модификатор environmentObject(_:), чтобы внедрить объект в окружение своих вложенных представлений.

SettingsView и ProfileView используют @EnvironmentObject для доступа к одному и тому же экземпляру UserSettings из среды и отображения и обновления имени пользователя. Представлениям не нужно знать, откуда берется объект или как он создается, им просто нужно указать тип объекта, который они ожидают.

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

@ObservedObject

Обертка свойства @ObservedObject позволяет вам создать и наблюдать наблюдаемый объект, который передается представлению в качестве параметра. Вы используете эту обертку свойства, когда хотите, чтобы ваше представление получило доступ к внешнему источнику данных, который принадлежит другому представлению или делегату приложения.

Наблюдаемый объект — это экземпляр пользовательского класса, который соответствует протоколу ObservableObject и может использоваться для хранения данных и логики приложения. Он также может использовать обертку свойств @Published, чтобы пометить некоторые из своих свойств как опубликованные, что означает, что они будут вызывать обновления представления при каждом изменении.

class TimerModel: ObservableObject < @Published var secondsElapsed = 0 var timer = Timer() init() < timer = Timer.scheduledTimer(withTimeInterval: 1, repeats: true) < _ in self.secondsElapsed += 1 >> > struct TimerView: View < @ObservedObject var timer: TimerModel var body: some View < Text("Seconds elapsed: \(timer.secondsElapsed)") >> struct ContentView: View < @StateObject var timer = TimerModel() var body: some View < TimerView(timer: timer) >>

В этом примере ContentView создает и владеет экземпляром TimerModel, который является наблюдаемым объектом, хранящим и обновляющим значение таймера. Представление использует @StateObject для сохранения объекта во время обновления представления и предотвращения многократной инициализации. Представление также передает объект в качестве параметра в TimerView, который использует его для отображения значения таймера. Представление использует @ObservedObject для наблюдения за изменениями объекта и соответствующего обновления.

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

Заключение

В этой статье мы объяснили различия между различными обертками свойств, которые SwiftUI предоставляет для потока данных. Мы также показали, как правильно использовать их в различных сценариях.

Если вы нашли опечатку — выделите ее и нажмите Ctrl + Enter! Для связи с нами вы можете использовать info@apptractor.ru.

Что такое SwiftUI и зачем он нужен

Через несколько лет, может быть уже через год или два, операционные системы Apple радикально изменятся внутри. И, конечно же, за этим последуют внешние изменения, и надеюсь, системы станут лучше. Небольшое число фрагментов этого будущего проходят обкатку в macOS Catalina и в последних версиях всех операционных систем Apple. И эти фрагменты впечатляют, похожи на магию, вызывают восхищение и немного глючат. Эта статья об одном из этих фрагментов. Для понимающих, но если вы хотите разобраться, тоже почитайте.

Что такое SwiftUI и зачем он нужен. Разработка приложений скоро выйдет на новый уровень. Фото.

Разработка приложений скоро выйдет на новый уровень

Что такое SwiftUI

Слухам о том что Apple вот-вот, чуть ли не на днях, выпустит совершенно сказочный 16,5-дюймовый MacBook Pro “без рамок вокруг экрана”, с какой-то особенной клавиатурой, не подверженной “эффекту бабочки”, скоро исполнится год. Интересно, что произойдет раньше – и какие новые каверзы встретятся нам в этом ноутбуке? А что это за “особенная клавиатура”? Ножницы, или что-то опять что-то своё, оригинальное и передовое? И опять, в точности как год назад, тайваньские аналитики сообщают о массовом производстве этих MBP, ставших легендарными еще до появления на свет, и отслеживают поставки разных комплектующих для них. В прошлый раз, правда, оказалось что все эти комплектующие были для какого-то другого ноутбука – но разведка и аналитика рискованные профессии. Существует ли это MacBook Pro вообще? Я думаю нет. Если думаете иначе, поделитесь в нашем Telegram-чате.

А вот о том, что в Apple работают над новой “начинкой” операционных систем компании, и кое-что из этих разработок уже доступно сторонним разработчикам в Xcode 11, и в этих кусочках будущего Apple впервые в своей истории использует старый добрый Swift иначе чем прежде – никто как-будто не слышал. Реактивное и декларативное программирование давно не новость, и используются в индустрии не один десяток лет – но Apple, как в старые добрые времена, использует их иначе. Не удивлюсь если через пару-другую лет именно “яблочное” воплощение всего этого станет образцом для подражания, пока же пусть недоброжелатели и дальше умирают со смеху, наблюдая за потугами “дамы с причудами” культивировать в своем стеклянном дворце “древние новшества”.

Что такое SwiftUI. SwiftUI на конференции WWDC 2019. Фото.

SwiftUI на конференции WWDC 2019

Нельзя сказать что про SwiftUI никто ничего не пишет. Им восторгаются. До совершенства ему (“UI” расшифровывается и переводится как “пользовательский интерфейс”, значит это “он”) еще развиваться и развиваться, но задуман он настолько красиво и оригинально, что остаться равнодушным не получается. Его равняют с грязью (перечисляя функции которые есть в XAML, Flutter и в других аналогах, но нет в “новорожденном”). Его называют заменой AppKit (в macOS) и UIKit (в iOS и iOS-подобных системах), но это неправда. SwiftUI это отдельный фреймворк и составная часть Xcode 11, он берет на себя часть функций AppKit и UIKit, при этом используя функции AppKit и UIKit для, например, отображения интерфейсов и для их взаимодействия с пользователем. По словам одного из инженеров группы SwiftUI, это временно: в будущем на месте AppKit или UIKit будет что-то другое.

А вот жизненный путь Interface Builder (IB) приближается к концу – SwiftUI делает то же что и IB, только интереснее и эффективнее. В 1986 году доктор компьютерных наук Жан-Мари Юллó написал программу InterfaceBuilder (на LISP) для компании ExperTelligence. А LISP это, если вы не в курсе, “декларативный вариант Фортрана”. Однажды, в 1987 или в 1988 году, Жана-Мари привезли в офис NeXT и познакомили с Джобсом. В 1988 году в NeXTSTEP 0.8, в состав которой входил IB написанный на Objective-C. IB использовался при написании первого в мире браузера WorldWideWeb, и много чего еще – очень заслуженный старичок. Последнее его обновление случилось 8 лет назад, в 2011 году. На WWDC 2011 года много чего обещали в него добавить, то что показали в кулуарах смотрелось менее фантастично чем Canvas в SwiftUI, но для того времени это было бы здорово – увы. Эти функции так и не пришли в IB, и уже не прийдут.

IB и до 2011 обновляли нечасто, а с 2008 года число пользователей Xcode стремительно росло, и почти 90% новых пользователей писали программы для iPhone OS (как тогда называлась iOS), а из них IB в своей работе использовал каждый третий. Так сложилось. Писать для iPhone OS начали задолго (за полгода) до первой бета-версии iPhone SDK, и в программах от Apple следов IB (файлов с расширением Xib) не было. Стив обещал что в SDK обязательно будет IB, но заставить этот инструмент работать с мобильной ОС оказалось труднее, чем перенести его из LISP в Objective-C. В первых бетах SDK его не было. Вплоть до iPhone SDK 2.0 он отчаянно глючил, а именно тогда люди осваивались с новой для них системой, набирались опыта и обретали привычки. IB для iPhone глючил еще очень долго, чуть ли не до 2011 года. В macOS некоторые тоже пишут интерфейсы в коде, вручную – я знаю только одного такого чудака. Там IB тоже не без глюков, но их немного.

Что такое SwiftUI. Использование Interface Builder в Xcode. Фото.

Использование Interface Builder в Xcode

В результате, пользовательские интерфейсы программисты iOS и iOS-подобных систем пишут либо с помощью IB, либо в исходном коде. Как пользователь IB с почти 20-летним стажем, утверждаю: увы, SwiftUI действительно “круче” чем IB. Это адекватная замена. Для рисующих интерфейсы в исходных кодах SwiftUI еще круче, потому что сейчас (в реальных программах SwiftUI пока еще почти не используется) для того, чтобы расположить на экране элемент интерфейса, пусть это будет текстовая строка, любая надпись, программист должен:

  • создать объект соответствующего типа;
  • вычислить координаты задающие положение объекта на экране;
  • разместить объект на экране;
  • задать значение объекта;
  • задать атрибуты текста (это несколько строк, как правило – шрифт, стиль, цвет и т.п.)

Это один из самых простых объектов – создание таблицы (списка) вручную может занять не одну сотню нетривиальных строк. Считается что в каждых ста строках нового кода, как минимум, должны оказаться две ошибки, которые не будут выявлены при тестировании и проявят себя только попав в руки пользователя. При увеличении числа строк в N раз число ошибок увеличивается в N в квадрате раз. В этой шутке есть доля шутки. Кстати, если вы не знаете что такое императивное программирование, вы только что увидели его “в лицо”.

В SwiftUI используется декларативная парадигма программирования, в рамках которой эта детализация и все эти тысячи строк становятся не нужны. Программист указывает что, и как относительно других элементов, должно быть выведено на экран (все это немедленно отображается на экране Canvas как будет выглядеть в реальной программе), а кроме того, инженеры Apple добавили в эту и без того впечатляющую картину сотни сумасшедших, но очень полезных и удобных, трюков. Результат магический – с нетерпением жду когда все это будет воплощено в окончательном варианте (через год или два).

Объекты окружения и стили SwiftUI

Окружение и стили SwiftUI являются двумя столпами официального фреймворка (декларативной структуры) от Apple. Не смотря на это, при первом запуске SwiftUI их совместное использование приводило к гарантированному падению приложения.

В частности, сбой происходил, когда мы использовали @EnvironmentObject внутри нашего определения стилей: когда безопасно использовать их вместе? Давайте выясним.

Заметка

Для нетерпеливых результаты в конце статьи .

Пример

Познакомьтесь с FSStyle , стилем кнопки, ожидающим объект среды ( FSEnvironmentObject ):

class FSEnvironmentObject: ObservableObject < @Published var title = "tap me" >struct FSStyle: ButtonStyle < @EnvironmentObject var object: FSEnvironmentObject func makeBody(configuration: Configuration) ->some View < Button(object.title) < >> >

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

struct ContentView: View < @StateObject var object = FSEnvironmentObject() var body: some View < Button("tap me") < >.buttonStyle(FSStyle()) .environmentObject(object) > >

..и все же, если мы запускали этот код в любой версии iOS до 14.5, он гарантированно давал сбой в 100% случаев с Fatal error: No ObservableObject of type FSEnvironmentObject found. .

Сбой происходит, как только объект окружения используется внутри метода makeBody (configuration: ) , и определение объекта, и исполнение makeBody (configuration: ) не имеют значения.

Есть два основных способа обойти ошибку: или привести стиль в соответствие с DynamicProperty (большое спасибо Линь Цин Мо за подсказку!), или вернуть View в методе makeBody (configuration: ) и заставить этот View читать объект окружения.

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

Тестовая установка

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

Продолжая на примере с ButtonStyle , вот полное приложение:

import UIKit import SwiftUI @UIApplicationMain class AppDelegate: UIResponder, UIApplicationDelegate < >class SceneDelegate: UIResponder, UIWindowSceneDelegate < var window: UIWindow? var object: FSEnvironmentObject = FSEnvironmentObject() func scene( _ scene: UIScene, willConnectTo session: UISceneSession, options connectionOptions: UIScene.ConnectionOptions ) < if let windowScene = scene as? UIWindowScene < let window = UIWindow(windowScene: windowScene) let contentView = ContentView().environmentObject(object) window.rootViewController = UIHostingController(rootView: contentView) self.window = window window.makeKeyAndVisible() >> > struct ContentView: View < var body: some View < Button("tap me") < >.buttonStyle(FSStyle()) > > struct FSStyle: ButtonStyle < @EnvironmentObject var object: FSEnvironmentObject func makeBody(configuration: Configuration) ->some View < Button(object.title) < >> > class FSEnvironmentObject: ObservableObject

Приложение состоит из одного экрана, на котором находится наш тестовый компонент/стиль.

  • мы используем жизненный цикл UIKit, потому что мы хотим запускать тесты также и на iOS 13
  • мы не используем @StateObject для объекта окружения, потому что данная обёртка свойств только для iOS 14+
  • единственная разница между тестированием ButtonStyle и другими стилями заключается в определении тела FSStyle и ContentView , все остальное остается прежним

Настройка CI/CD

Мы будем тестировать двенадцать версий iOS, от iOS 13.0 до iOS 14.5, и все восемь стилей, поддерживающих настройку.

Тестировать каждую комбинацию вручную было бы довольно сложно, вместо этого мы можем позволить провайдеру CI/CD сделать всю тяжелую работу за нас. Подойдет любая установка CI/CD, вот как различные версии Xcode/iOS были распределены для этого исследования:

  • macOS 10.14
    • iOS 13.0, Xcode 11.0
    • iOS 13.1, Xcode 11.1
    • iOS 13.2, Xcode 11.2
    • macOS 10.15
      • iOS 13.3, Xcode 11.3.1
      • iOS 13.4, Xcode 11.4.1
      • iOS 13.5, Xcode 11.5
      • iOS 13.6, Xcode 11.6
      • iOS 13.7, Xcode 11.7
      • iOS 14.0, Xcode 12.0.1
      • iOS 14.1, Xcode 12.1
      • iOS 14.2, Xcode 12.2
      • iOS 14.3, Xcode 12.3
      • macOS 11.4:
        • iOS 14.4, Xcode 12.4
        • iOS 14.5, Xcode 12.5

        Поскольку в тестовом приложении есть только один экран, на котором сразу отображается тестируемый компонент, все, что нужно для прохождения теста, — это запустить приложение и не вылететь тотчас же. Вот результат:

        Style ���� / iOS ����

        �� = вылет, ✅ = пройдено. * Стиль доступен с iOS 14.

        • все стили, кроме ButtonStyle , поддерживают @EnvironmentObject с iOS 14.0
        • начиная с iOS 14.5, все стили, включая ButtonStyle , поддерживают @EnvironmentObject

        Выводы

        Причина, по которой комбинация styles + @EnvironmentObject не поддерживалась с самого начала, вероятно, останется внутри команды SwiftUI, однако это могло быть намеренно:

        Глядя на то, как применяются стандартные стили SwiftUI, помимо нескольких параметров, передаваемых через Configuration , бОльшая часть изменяемых компонентов приходит из EnvironmentValues , например @Environment (\.IsEnabled) , @Environment (\.font) и @Environment (\ . controlProminence) .

        В отличие от @EnvironmentObject , EnvironmentValues ​​поддерживается (без вылета!) с iOS 13.0, поэтому я рекомендую их при добавлении динамики в наши пользовательские стили.

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

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

        Еще одно место, где это могло быть проблемой — это модификаторы View, однако и EnvironmentValues , и @EnvironmentObject поддерживаются (без �� ) из iOS 13.0.

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *