Декларативный UI в Android и iOS

Разберем, как Google и Apple предлагает разрабатывать интерфейсы в следующие несколько лет.

В закладки

В мае 2019 года Google представила UI-фреймворк Jetpack Compose. Apple через месяц на WWDC показала SwiftUI. Подходы схожи между собой, которые со временем должны прийти на смену ConstraintLayout и AutoLayout.

В iOS UI-компоненты создаются с помощью Interface Builder, рекомендуется использовать Storyboard — в одном файле можно сделать не только верстку одного экрана, а сразу многих, при это связав их между собой переходами, что очень наглядно.

Напрямую с XML в iOS работать не нужно — все делается через редактор. В Android исторически UI создавался напрямую в XML-формате, есть возможность просмотреть сразу предпросмотр результата. Аналог Storyboard в Android — Navigation Component.

Jetpack Compose

Для того, чтобы воспользоваться новым фреймворком, придется установить Android Studio 4.0 Preview. Пока рано использовать этот подход в реальных проектах — подождать стоит как минимум год. За это время Google соберет все ошибки фреймворка и подготовит его для релиза. На официальном сайте для разработчиков есть tutorial, в котором сейчас 3 урока, и документация. В документации достаточно подробно разъясняются базовые вещи работы с UI с помощью Compose. По окончании уроков получается экран с изображением, стилизованным текстом в стиле Material.

@Composable fun NewsStory() { val image = +imageResource(R.drawable.header) MaterialTheme { Column( modifier = Spacing(16.dp) ) { Container(modifier = Height(180.dp) wraps Expanded) { Clip(shape = RoundedCornerShape(8.dp)) { DrawImage(image) } } HeightSpacer(16.dp) Text("A day in Shark Fin Cove") Text("Davenport, California") Text("December 2018") } } }

Небольшой пример экрана с изображением и тремя строчками текста.

Использование в Activity.

class MainActivity : AppCompatActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContent { NewsStory() } } }

Compose входит в состав Jetpack, что делает возможным использование даже в старых версиях Android. Использование Kotlin позволяет писать UI в декларативном стиле прямо в коде вместо XML. В случае, если мы хотим обновить UI при действиях пользователя, обновлении данных из сети, нам нужно сделать data class с аннотацией @Model, чтобы свойства были наблюдаемыми. Затем при изменении этого класса UI-компонент автоматически обновится актуальными данными. Фреймворк не только задает правила создания экранов, он еще и говорит нам как правильно работать с данными. А работа с ними в Android — большая тема для дискуссий. Так вот Compose говорит нам, что мы должны разрабатывать UI c учетом так называемого “однонаправленного поток данных”. Compose проясняет кто отвечает за состояние и как должны обрабатываться события, подобно тому, как это обрабатывает React. Разработка ведется с тестовыми данными в режиме Preview. Этот код предназначен лишь для разработки и не вызовется при запуске приложения на устройстве или эмуляторе, определить этот код можно по аннотации @Preview.

@Preview @Composable fun DefaultPreview() { NewsStory() }

Для обратной совместимости можно использовать аннотацию @GenerateView и получить view по id.

@Composable @GenerateView fun Greeting(name: String) {/* ... */} <GreetingView android:id="@+id/greeting" app:name="@string/greeting_name" /> val greeting: GreetingView = findViewById(R.id.greeting) greeting.setName("Jim")

SwiftUI

У Apple так же есть набор уроков, я бы даже назвал это курсом — уроков значительно больше, чем в Compose, они подробнее, содержат готовые проекты, у каждого указано ориентировочное время выполнения. В конце урока можно ответить на проверочные вопросы. Примеры хоть и тестовые, но они далеко ушли от “Hello, world”. Сайт оформлен очень удобно и красиво — анимации переходов, все на высоте. Рекомендую хотя бы взглянуть.

Простой пример уже в SwiftUI.

struct ContentView: View { var body: some View { VStack(alignment: .leading, spacing: 8) { Image("image") .cornerRadius(8) VStack(alignment: .leading, spacing: 2) { Text("A day in Shark Fin Cove") .font(.title) Text("Davenport, California") Text("December 2018") Spacer() } } .padding() } }

Предпросмотр можно включить, написав так

struct ContentView_Previews: PreviewProvider { static var previews: some View { ContentView() } }

Релиз SwiftUI получился более масштабным среди разработчиков своей платформы и далее я поясню почему.

Во-первых, решение уже можно использовать в production. Да, нет обратной совместимости с версиями меньше iOS 13, но Apple редко показывает что-то с претензией на далекое будущее. Раз показали, значит, уверены, что это будет работать, пусть и не на все 100%. Здесь могу заметить, что SwiftUI хоть и полноценно доступен в текущих версиях Xcode, тем не менее, еще есть компоненты, которых не хватает для полноценной разработки, документация не идеальная, да и мало кто готов отбросить порядка 10-15% пользователей старых операционных систем.

Во-вторых, вместе со SwiftUI Apple представила фреймворк для реактивного программирования Combine (да, в Swift достаточно популярен RxSwift, как и RxKotlin в Android) и другие интересные фичи Swift (например, property wrapper), которые стоит использовать совместно со SwiftUI.

В-третьих, реализация UI с помощью SwiftUI достаточно сильно меняет архитектуру приложения — надобность контроллера в MVC отпадает. Навигация происходит сразу во view. Действия на UI обрабатываются на View, изменяя данные.

В-четвертых, создавать UI можно как и написанием кода, так и набрасыванием элементов в редакторе — код подставится автоматически. Это помогает в процессе обучения, когда еще плохо помнишь наизусть разные UI-компоненты. В Jetpack Compose создание только через код.

Доступные компоненты в SwiftUI

Так же немаловажный фактор — SwiftUI доступен не только на iOS, но и на watchOS, macOS, tvOS.

Выводы

Google традиционно показывает новинки в альфа-версиях, Apple же наоборот — инструменты, которые можно использовать здесь и сейчас. У обоих подходов есть свои плюсы и минусы. Но уже понятно, что постепенно в ближайшие несколько лет разработка UI новых проектов будет только на Compose и SwiftUI. Фреймворки декларативного UI уменьшают порог входа для новичков, ускоряют процесс создания приложений. Подход не является чем-то новым в разработке — React, Flutter, так и другие open source-решения предлагали похожие решения и ранее, но Google и Apple постарались сделать все проще и лучше.

На текущий момент я бы не советовал использовать даже доступный SwiftUI в production, но с выходом новой версии iOS летом, фреймворк наверняка станет стабильнее.

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

Материал опубликован пользователем.
Нажмите кнопку «Написать», чтобы поделиться мнением или рассказать о своём проекте.

Написать
{ "author_name": "Ivan Vavilov", "author_type": "self", "tags": ["ui","ios","android"], "comments": 8, "likes": 0, "favorites": 8, "is_advertisement": false, "subsite_label": "dev", "id": 99593, "is_wide": true, "is_ugc": true, "date": "Sun, 29 Dec 2019 15:33:26 +0300", "is_special": false }
0
{ "id": 99593, "author_id": 140146, "diff_limit": 1000, "urls": {"diff":"\/comments\/99593\/get","add":"\/comments\/99593\/add","edit":"\/comments\/edit","remove":"\/admin\/comments\/remove","pin":"\/admin\/comments\/pin","get4edit":"\/comments\/get4edit","complain":"\/comments\/complain","load_more":"\/comments\/loading\/99593"}, "attach_limit": 2, "max_comment_text_length": 5000, "subsite_id": 235819, "last_count_and_date": null }
8 комментариев
Популярные
По порядку
Написать комментарий...
1

Судя по интернетам - swiftUI сам по себе - довольно стабилен. Другой вопрос, что сама iOS 13 получилась с кучей мелких багов - причём настолько, что Эппл декларировал смену процесса разработки для предотвращения нынешней ситуации

Ответить
0

А можно несколько аргументов в сторону этого заявления “На текущий момент я бы не советовал использовать даже доступный SwiftUI в production”? (если речь идет о новых разработках).
SwiftUI ведь совместим с UIkit. 

Ответить
0

Скорее всего, имеется в виду, что со SwiftUI сейчас вы потеряете половину пользователей, так как для неё требуется iOS 13, а до 95% проникновения она дойдёт только через год — к моменту выпуска iOS 14.

Обычно сам Apple рекомендует поддерживать две последние версии — в данный момент это iOS 12 и iOS 13.  Но большинство заказчиков и на это не готовы, в требованиях до сих пор ставят «включая поддержку iOS 10».

Ответить
1

На декабрь 2019 доля iOS13 в Северной Америке более 70%, в мире – более 65%.
К лету эта цифра может значительно вырасти (как раз время на разработку и тесты)
Плюс стоит учитывать особенности по ЦА, региону, продукту и тд.

 
 Насчет поддержки согласен, если есть что и кого поддерживать. 

Ответить
1

Я сам заказчиков агитирую оставить только iOS 13 и отсечь всех, кто ниже (из-за RealityKit, в котором удобнее организовывать совместную работу в AR — то, что сейчас все хотят), но никто не соглашается.  Все хотят минимум iOS 10, в крайнем случае 11.

Ответить
1

Забавные заказчики. Весь мир имеет на девайсах iOS 12 + 13. Что ж это за проекты они такие хотят. Может они и скевоморфизм просят? )

Надо давить на SwiftUI. Причем не только даже на клиентов сколько и на девов. А то ходили тут истории про девов, которые хейтили SwiftUI за кривость, а на вопрос пробовали ли что-то покодить отвечали "нет".

Ответить
0

Обычные заказчики с Upwork и FL.ru.

Нет, все хотят чтобы на iOS 13 было красиво, но чтобы iOS 10 тоже поддерживалось.  И желательно, чтобы ещё и Android за ту же цену.

Сейчас вот на Flutter смотрю для таких

Ответить
0

У Google есть же ещё Flutter https://flutter.dev — полностью декларативный UI для всех платформ.

Работает уже сейчас практически на всех Android и iOS-устройствах, до которых можно дотянуться. Компилирует из Dart нативный Objective C/Swift код для Xcode и Java/Kotlin для Android Studio.
 
 На конференции Flutter Interact https://developers.google.com/events/flutter-interact Google рассказывал, что на проект перебрасываются лучшие программисты, персонал вырос вдвое и что это стратегическое будущее — работа приложений на любых устройствах от телефонов до Smart Display. 

 Не совсем понятно, как они будут сосуществовать с Jetpack Compose.

Ответить
{ "page_type": "article" }

Прямой эфир

[ { "id": 1, "label": "100%×150_Branding_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox_method": "createAdaptive", "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfl" } } }, { "id": 2, "label": "1200х400", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfn" } } }, { "id": 3, "label": "240х200 _ТГБ_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fizc" } } }, { "id": 4, "label": "Article Branding", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cfovx", "p2": "glug" } } }, { "id": 5, "label": "300x500_desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "ezfk" } } }, { "id": 6, "label": "1180х250_Interpool_баннер над комментариями_Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "ffyh" } } }, { "id": 7, "label": "Article Footer 100%_desktop_mobile", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjxb" } } }, { "id": 8, "label": "Fullscreen Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjoh" } } }, { "id": 9, "label": "Fullscreen Mobile", "provider": "adfox", "adaptive": [ "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fjog" } } }, { "id": 10, "disable": true, "label": "Native Partner Desktop", "provider": "adfox", "adaptive": [ "desktop", "tablet" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyb" } } }, { "id": 11, "disable": true, "label": "Native Partner Mobile", "provider": "adfox", "adaptive": [ "phone" ], "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "clmf", "p2": "fmyc" } } }, { "id": 12, "label": "Кнопка в шапке", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "bscsh", "p2": "fdhx" } } }, { "id": 13, "label": "DM InPage Video PartnerCode", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox_method": "createAdaptive", "adfox": { "ownerId": 228129, "params": { "pp": "h", "ps": "bugf", "p2": "flvn" } } }, { "id": 14, "label": "Yandex context video banner", "provider": "yandex", "yandex": { "block_id": "VI-223676-0", "render_to": "inpage_VI-223676-0-1104503429", "adfox_url": "//ads.adfox.ru/228129/getCode?pp=h&ps=bugf&p2=fpjw&puid1=&puid2=&puid3=&puid4=&puid8=&puid9=&puid10=&puid21=&puid22=&puid31=&puid32=&puid33=&fmt=1&dl={REFERER}&pr=" } }, { "id": 15, "label": "Баннер в ленте на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byudx", "p2": "ftjf" } } }, { "id": 16, "label": "Кнопка в шапке мобайл", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "adfox": { "ownerId": 228129, "params": { "p1": "byzqf", "p2": "ftwx" } } }, { "id": 17, "label": "Stratum Desktop", "provider": "adfox", "adaptive": [ "desktop" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvb" } } }, { "id": 18, "label": "Stratum Mobile", "provider": "adfox", "adaptive": [ "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "pp": "g", "ps": "bugf", "p2": "fzvc" } } }, { "id": 19, "disable": true, "label": "Тизер на главной", "provider": "adfox", "adaptive": [ "desktop", "tablet", "phone" ], "auto_reload": true, "adfox": { "ownerId": 228129, "params": { "p1": "cbltd", "p2": "gazs" } } }, { "id": 20, "label": "Кнопка в сайдбаре", "provider": "adfox", "adaptive": [ "desktop" ], "adfox": { "ownerId": 228129, "params": { "p1": "cgxmr", "p2": "gnwc" } } } ] { "page_type": "default" }