В мае на Google I/O 2019 было объявлено о новом фреймворке для разработки декларативного UI под Android с названием Jetpack Compose. Через месяц на WWDC 2019 было объявлено о декларативном UI фреймворке под IOS с названием SwiftUI. После этих конференции стало понятно, к чему стремится мобильная разработка, и мне захотелось разобраться, что можно сделать с помощью этих фреймворков на данный момент и в чем разница между ними.

Беглый обзор


После беглого обзора стало понятно о наличии крутой документации в IOS



и о полном отсутствии документации в android. Для ознакомления с фреймворком на официальной странице советуют посмотреть демо приложение.


Hello World


Начать использовать SwiftUI очень просто, достаточно обновить macOS до beta версии, установить Xсode 11 Beta, и создать новый проект, при этом выбрав галочку “Use SwiftUI”.



В случае с Jetpack Compose все сложнее. Нам нужно скачать последнюю сборку с Jetpack. И на основе модуля ui-demos, методом тыка, разобраться, как работать c фраймворком и какие зависимости необходимы. Очень печальное первое впечатление от Compose по сравнению с SwiftUI.



Пример Compose

    @Composable
    fun HelloWorld() {
        Text(text = "Hello world")
    }

Пример SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello SwiftUI!")
    }
}

Верстка UI выглядит очень схоже, что логично, так как она стремится к декларативному виду.

Визуальный редактор


Со SwiftUi поставляется визуальный редактор(canvas),
который:

— перерисовывeт экран моментально после изменения кода;



— умеет показывать сразу же несколько девайсов;



— дает возможность изменять атрибуты ui элементов из редактора;


— и самое интересное и крутое — можно запустить код ui из редактора и посмотреть, как работает экран.


В случае с Jetpack Compose никакого визуального редактора нет.

Работа со списками


Для работы в SwiftUI просто создаем view строки

struct ListRow: View {
    let number:Int
    
    var body: some View {
        Text.init(verbatim: "Text \(number)")
    }
}

и вставляем в список.

  List(Array(0...44)) { number in
                ListRow.init(number: number)
            }

Список готов.


В Compose пока нет виджета работающего со списками. На первый взгляд, можно воспользоваться Column.

Column {
   listOf(
       "Артем",
       "Святослав",
       "Женя",
       "Никита",
       "Алина",
       "Леша",
       "Вадим",
       "Паша",
       "Ваня"
   )
       .forEach { name ->
           Text(text = name, style = +themeTextStyle { h2 })
       }
} 

Но он не скролящийся.


Что самое печальное, даже пример от Google не доработан и не скролится.


Иерархия view


В SwiftUI корневым элементом на смену UIViewController становится новый компонент View с новым жизненным циклом. Это говорит о том, что Apple, проанализировав плюсы и минусы реализации UIViewController, попыталась сделать все по-новому, реализовав лучшие черты.

struct ContentView : View {
   var body: some View {
        Text("ContentView2  ")
            .onDisappear(){
                print("onDisappear")
            }
            .onAppear{
                print("onAppear")
            }
    }
}

В случае же с Compose корневыми элементами остаются старый добрый Activity или Fragment, вместо привычной загрузки XML.

class MainActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)
    }
}

Идет загрузка виджетов из Compose.

class ComposeActivity : Activity() {
    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContent {
            CraneWrapper {
                MaterialTheme {
                    Text(text = "Hello world")
                }
            }
        }
    }
}

Это ведет к тому, что в Android не избавляются от легаси корневых вьюх. Просто старыe UI компоненты теперь общаются не с XML, а с Compose виджетами.

Навигация


Для навигации в SwiftUI необходимо указать, какая view должна открываться по нажатию на кнопку.

NavigationButton(destination: LandmarkDetail()) {
                   LandmarkRow(landmark: landmark)
               } 

Внутри Compose реализации навигации нет. Но наверное она и не должна быть, так как корневым элементом являeтся Activity или Fragment, в которых уже давным давно накостылена реализована навигация.

Поддерживаемая версия


SwiftUI работает с IOS 13, выходящей в сентябре, которая уже не будет поддерживаться на iPhone 5S, iPhone6, iPhone 6 Plus. Разработчикам стоит задуматься, насколько они будут готовы отказаться от этих моделей.

Compose планирует поддерживаться на любой версии Android.

Заключения


После конференций я думал, что появилось два новых мощных фреймворка для ui. В итоге получилось, что Apple сделал крутое решение, от которого у многих “состояние экзальтации”. А Google анонсировала фреймворк, который настолько сырой, что не понятно, как будут работать многие моменты даже на уровне концепций.

Комментарии (10)


  1. saag
    24.06.2019 06:00

    У меня ощущение, что compose jetpack это Flutter, только на котлине


    1. sergeyfitis
      24.06.2019 09:06

      да так и есть, они тесно работают с коммандой Flutter, если посмотреть в репозиторий, там даже дарт код есть тыц


  1. andrew8712
    24.06.2019 09:04

    Не забываем, что у Google есть Flutter, который уже работает. Приложения на Flutter запускаются на iOS 8. Так что Apple тут в роли догоняющего


    1. xander27
      24.06.2019 09:49

      del


  1. artemgapchenko
    24.06.2019 10:13

    Лучше знакомство с Compose было начать с видео с Google IO 2019, они там по ходу презентации несколько раз прямо сказали — ребята, это даже не альфа, ни в коем случае не думайте о том, что это скоро будет production-ready. Мы анонсируем Compose сейчас для того, чтобы как можно раньше начать собирать фидбек и прорабатывать концепцию.


  1. JediPhilosopher
    24.06.2019 12:35

    Я к андроид разработке мало отношения имею, может глупость скажу, но для меня это выглядит как какой-то шаг назад. Ну вот сто лет назад был Swing, и в нем надо было все лайауты писать в коде. И все ругались, мол, неудобно, неправильно смешивать код и визуал и т.п. Затем пришел андроид, и в нем появился мощный визуальный редактор и описание экранов в хмл, и все радовались. А теперь пришел вот этот Jetpack, и мы снова UI пишем в коде. В чем смысл? А потом будет еще одна библиотека и мы опять начнем интерфейс делать на каком-нибудь языке разметки?


    1. agent10
      24.06.2019 21:14

      Думая, что всё просто. Когда появились первые телефоны, то они были примерно одного размера и логика/динамика были тривиальными. Обычной разметки было достаточно. Дальше стали появляться другие форм-факторы, размеры, разрешения и накрученная логика. Т.е. опять вернулись к сложным и динамично меняющимся «окнам». Отсюда и возврат к истокам.


    1. dector
      25.06.2019 07:48

      Дело не в том, где мы описываем UI, а в том, как мы это делаем.

      Compose, SwiftUI, Flutter, React, Vue, Litho, Svelte и т.д. и т.п. реализуют подход под названием Declarative UI. Что это значит — в двух словах можно прочесть, например, на сайте Flutter'a.

      Соавтор Compose Leland Richardson собрал материалы (в основном из мира React), которые могут помочь мобильным разработчикам лучше понять новый подход. А тулинг — дело наживное (для Compose, например, обещают сделать его не хуже текущего, но не скоро, конечно же).

      Статья не корректно сравнивает существующий продукт (SwiftUI) и продукт в состоянии proof-of-concept (Compose). artemgapchenko совершенно верно заметил, что сначала следовало бы посмотреть первоисточник (анонс на Google I/O).

      Также в Slack-чате языка Kotlin есть канал #compose, где можно внести предложение или задать вопрос, получив ответ из первых рук (Romain Guy и Leland Richardson регулярно принимают участь в обсуждениях).


      1. ivanlardis Автор
        26.06.2019 13:13

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

        Насчет того, что сначала стоило посмотреть первоисточник — я с первоисточника и начал. И я понимал, что Compose в очень ранней стадии разработки и понимал, что гугл сказал, что хочет собрать обратную связь от разработчиков. Но при этом всем, когда начал смотреть Compose, в голове держал, то что Google уже создал Flutter, который вызвал большой интерес у Android разработчиков. И что у Google уже есть обратная связь по Flutter от тех же разработчиков, от которых просит обратную связь по Compose. И ждал от Compose, то что он будет реализован на основе отзывов о Flutter, хоть и на уровне концепции. А по факту, сейчас Compose — это пара полурабочих вьюх. Какого отзыва Google ждет и может ждать на основе такого? Складывается ощущение, что Google было важно анонсировать хоть что-то.

        Насчет статьи надеюсь, хоть кому-то она была полезна.


    1. vikarti
      26.06.2019 05:20

      Собственно и для Андроид — Compose не первый.
      Есть например Anko от Jetbrains

       relativeLayout {
              padding = dip(16)
      
              val w = dip(200)
              val loginEditId = 155;
              val loginEdit = editText {
                  id = loginEditId
                  hint = "Login"
              }.layoutParams { centerInParent(); width = w }
      
              button("Sign up") {
                  textSize = 18f
                  onClick { doWork(loginEdit.getText().toString()) }
              }.layoutParams {
                  below(loginEditId); sameLeft(loginEditId);
                  width = w; topMargin = dip(8)
              }
          }


      или так

      constraintLayout {
      
          val sessionStart = textView {
              id = R.id.session_start
              textSize = 18f
              textColor = theme.getColor(R.attr.colorAccent)
          }
      
          val sessionTitle = textView {
              id = R.id.session_title
              textSize = 18f
              textColor = Color.BLACK
          }.lparams(0, wrapContent)
      
          textView {
              id = R.id.session_details
              textSize = 16f
          }.lparams(0, wrapContent)
      
          applyConstraintSet {
              // Connect without block
              // You may use view id or view itself to define connections
              connect(
                      START of R.id.session_start to START of PARENT_ID margin dip(10),
                      TOP of sessionStart to TOP of PARENT_ID margin dip(10)
              )
      
              // constraint configuration on view
              sessionTitle {
                  connect(
                          START to START of PARENT_ID margin dip(SESSION_LIST_HEADER_MARGIN),
                          TOP to TOP of PARENT_ID margin dip(10),
                          END to END of PARENT_ID margin dip(10),
                          BOTTOM to TOP of R.id.session_details
                  )
      
                  horizontalBias = 0.0f
                  defaultWidth = MATCH_CONSTRAINT_WRAP
              }
      
              // constraint configuration on view Id
              R.id.session_details {
                  connect(
                          START to START of PARENT_ID margin dip(SESSION_LIST_HEADER_MARGIN),
                          TOP to BOTTOM of sessionTitle margin dip(2),
                          END to END of PARENT_ID margin dip(10),
                          BOTTOM to BOTTOM of PARENT_ID margin dip(2)
                  )
      
                  horizontalBias = 0.0f
                  defaultWidth = MATCH_CONSTRAINT_WRAP
              }
          }
      }

      Preview-плагин для Android Studio есть.
      Списки — ну поддержка RecyclerView более менее штатная и вообще совместимость со «стандартной» моделью для Android — получше.
      Чем Compose СЕЙЧАС лучше Anko?