Является ли Kotlin Multiplatform будущим кроссплатформенной разработки? Советы о том, как начать

Опубликовано: 2021-01-29

В настоящее время мы можем наблюдать тенденцию в мобильной разработке к более быстрому выпуску приложений. Было много попыток сократить время разработки за счет совместного использования общих частей кода на разных платформах, таких как Android и iOS. Некоторые решения уже завоевали популярность, другие находятся в стадии разработки. Сегодня я хотел бы обсудить один из новейших подходов из второй группы — Kotlin Multiplatform Mobile (сокращенно KMM).

Что такое Kotlin Multiplatform Mobile?

KMM — это SDK, который в первую очередь нацелен на совместное использование бизнес-логики между платформами — часть, которая в большинстве случаев должна быть одинаковой. Это достигается благодаря набору нескольких компиляторов для общего модуля. Например, для Android используется вариант Kotlin/JVM, а для iOS — Kotlin/Native. Затем к типичным проектам нативных приложений можно добавить общий модуль, а разработчики, ответственные за пользовательский интерфейс, могут сосредоточиться на предоставлении пользователям наилучших возможностей в знакомой им среде — Android Studio для Android и Xcode для iOS.

Мультиплатформа Kotlin против Flutter

В настоящее время одним из самых популярных решений для кроссплатформенной разработки приложений является Flutter. Он ориентирован на правило «написать одно приложение и запускать его везде», которое работает, но только для простых приложений. В реальных сценариях разработчикам часто все равно приходится писать собственный код для каждой платформы, чтобы заполнить пробелы , например, когда какой-то плагин отсутствует. При таком подходе приложение выглядит одинаково на разных платформах, что иногда желательно, но в некоторых случаях может нарушать определенные принципы дизайна.

Значок службы кроссплатформенной разработки

Готовы создать собственное приложение?

Выберите флаттер

Хотя они могут показаться похожими, Kotlin Multiplatform не является кросс-платформенным решением — он не пытается изобретать велосипед. Разработчики по-прежнему могут использовать инструменты, которые они знают и любят. Это просто упрощает процесс повторного использования частей кода , которые ранее должны были быть написаны несколько раз, таких как выполнение сетевых запросов, хранение данных и другой бизнес-логики.

Плюсы и минусы мультиплатформы Kotlin

Плюсы КММ :

  • Разработанное приложение на 100% нативно для каждой платформы — его легко интегрировать с используемым в настоящее время кодом и сторонними библиотеками.
  • Простота в использовании — почти все разработчики Android уже используют Kotlin, поэтому для начала им требуется очень мало дополнительных знаний.
  • Пользовательский интерфейс можно разделить для каждой целевой платформы — приложение будет соответствовать любой конкретной экосистеме.
  • Общая логика позволяет разработчикам добавлять новые функции и исправлять ошибки в обеих операционных системах одновременно.

Минусы КММ :

  • Многие компоненты все еще находятся на стадии альфа/бета и потенциально могут быть нестабильными или изменяться в будущем.

Какие компании используют КММ?

Согласно официальному сайту, компании проявляют все больший интерес к этой технологии, и список постоянно становится все длиннее и длиннее. Среди них такие известные бренды, как Autodesk, VMWare, Netflix или Яндекс.

Как начать работу с мультиплатформой Kotlin?

Лучшее место для получения подробной информации — это официальное руководство, но в этой статье я хотел бы показать пример, который довольно прост, но более интересен, чем просто «Hello World», который будет получать и отображать приложения. последний комикс Рэндалла Манро (под лицензией CC BY-NC 2.5) с названием из xkcd.com API.

Особенности, которые необходимо охватить:

  • Настройка проекта
  • Сеть в общем модуле
  • Простой пользовательский интерфейс для Android и iOS

Примечание. Я хотел, чтобы этот пример был как можно проще для чтения как разработчиками Android, так и iOS, поэтому в некоторых местах я намеренно опустил некоторые передовые практики для конкретных платформ, просто чтобы было понятно, что происходит.

Настройка проекта

Во-первых, убедитесь, что у вас установлены последние версии Android Studio и Xcode , потому что они оба будут необходимы для сборки этого проекта. Затем в Android Studio установите плагин KMM. Этот плагин упрощает многое — чтобы создать новый проект, просто нажмите «Создать новый проект» и выберите «Приложение KMM».

Создайте новый проект Kotlin Multiplatform Mobile.

После создания проекта перейдите к файлу build.gradle.kts в общем каталоге . Здесь вы должны указать все необходимые зависимости. В этом примере мы будем использовать ktor для сетевого уровня, kotlinx.serialization для анализа ответов json от серверной части и сопрограммы kotlin для асинхронного выполнения всего этого.

Для простоты ниже я привожу списки, показывающие все зависимости, которые необходимо добавить к уже существующим. Когда вы добавляете зависимости, просто синхронизируйте проект (появится подсказка). Сначала добавьте плагин сериализации в раздел плагинов.

 плагины {
   kotlin("plugin.serialization") версия "1.4.0"
}

Затем добавьте зависимости.

 наборы источников {
   val commonMain, получив {
       зависимости {
           реализация ("org.jetbrains.kotlinx: kotlinx-serialization-json: 1.0.0")
           реализация ("org.jetbrains.kotlinx: kotlinx-coroutines-core: 1.3.9-native-mt-2")

           реализация ("io.ktor: ktor-client-core: 1.4.1")
           реализация ("io.ktor: ktor-client-json: 1.4.1")
           реализация ("io.ktor: ktor-client-serialization: 1.4.1")
       }
   }
   val androidMain, получив {
       зависимости {
           реализация ("io.ktor: ktor-client-android: 1.4.1")
       }
   }
   val iosMain, получив {
       зависимости {
           реализация("io.ktor:ktor-client-ios:1.4.1")
       }
   }
}

Стоит отметить, что на момент написания этой статьи были некоторые проблемы со стабильной версией библиотеки сопрограмм для iOS — поэтому используемая версия имеет суффикс native-mt-2 (что означает нативную многопоточность). Вы можете проверить текущий статус этого вопроса здесь.

Сеть в общем модуле

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

 импортировать kotlinx.serialization.Serializable

@Serializable
класс данных XkcdResponse(
   значение изображения: строка,
   val title: Строка,
   день: Int,
   val месяц: Int,
   год: Int,
)

Далее нам нужно создать класс, представляющий API с HTTP-клиентом . Если мы не предоставили все поля, представленные в json, мы можем использовать свойство ignoreUnknownKeys , чтобы сериализатор мог игнорировать отсутствующие. В этом примере есть только одна конечная точка, представленная приостановленной функцией. Этот модификатор сообщает компилятору, что эта функция является асинхронной. Я опишу это больше с кодом для конкретной платформы.

 импортировать io.ktor.client.*
импортировать io.ktor.client.features.json.*
импортировать io.ktor.client.features.json.serializer.*
импортировать io.ktor.client.request.*

класс XkcdApi {
   частный val baseUrl = "https://xkcd.com"

   частное значение httpClient = HttpClient() {
       установить (JsonFeature) {
           сериализатор = KotlinxSerializer(
               kotlinx.serialization.json.Json {
                   игнорироватьUnknownKeys = истина
               }
           )
       }
   }

   приостановить веселье fetchLatestComic() =
       httpClient.get<XkcdResponse>("$baseUrl/info.0.json")

}

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

 класс данных ComicModel(
   val URL-адрес изображения: строка,
   val title: Строка
)

Последней частью этого уровня является создание варианта использования, который инициирует сетевой запрос, а затем сопоставляет ответ с локальной моделью.

 класс GetLatestComicUseCase (частное значение xkcdApi: XkcdApi) {
   приостановить забавный запуск () = xkcdApi.fetchLatestComic ()
       .let {ComicModel(it.img, it.title)}
}

Простой пользовательский интерфейс для Android

Пришло время перейти в каталог androidApp — здесь хранится родное приложение для Android. Во-первых, нам нужно добавить некоторые специфичные для Android зависимости в другой файл build.gradle.kts , расположенный здесь. Опять же, в приведенном ниже листинге показаны только те зависимости, которые следует добавить к уже существующим. Это приложение будет использовать архитектуру Model-View-ViewModel (первые две строки) и Glide для загрузки комического изображения из возвращенного URL-адреса (вторые две строки).

 зависимости {
   реализация ("androidx.lifecycle: lifecycle-viewmodel-ktx: 2.2.0")
   реализация ("androidx.lifecycle: lifecycle-livedata-ktx: 2.2.0")
   реализация ("com.github.bumptech.glide:glide:4.11.0")
   annotationProcessor("com.github.bumptech.glide:compiler:4.11.0")
}

По умолчанию вновь созданный проект должен содержать MainActivity и его файл макета activity_main.xml . Давайте добавим к нему несколько представлений — один TextView для заголовка и один ImageView для самого комикса.

 <?xml версия="1.0" кодировка="utf-8"?>
<LinearLayout xmlns:andro
   андроид:
   Android: layout_width = "match_parent"
   андроид: layout_height = "match_parent"
   андроид: гравитация = "центр"
   андроид: ориентация = "вертикальный">

   <текстовый вид
       андроид:
       Android: layout_width = "wrap_content"
       android:layout_height="wrap_content"/>

   <Просмотр изображения
       андроид:
       Android: layout_width = "match_parent"
       android:layout_height="wrap_content"/>

</Линейный макет>

Затем нам понадобится какое-то представление о состоянии приложения — это может быть загрузка нового комикса, его отображение или мы можем столкнуться с ошибкой при загрузке.

 состояние запечатанного класса {
   Загрузка объекта: Состояние()
   класс Успех (значение результата: ComicModel): Состояние ()
   Ошибка объекта: Состояние()
}

Теперь добавим минимальную ViewModel, используя ранее созданную бизнес-логику . Все классы можно импортировать. MutableLiveData — это наблюдаемое поле — представление будет отслеживать его изменения и соответствующим образом обновляться. viewModelScope — это область сопрограммы, связанная с жизненным циклом модели представления — в случае закрытия приложения оно автоматически отменяет ожидающие задачи.

 класс MainViewModel : ViewModel() {
   private val getLatestComicUseCase = GetLatestComicUseCase(XkcdApi())
   val comic = MutableLiveData<State<ComicModel>>()

   весело fetchComic() {
       viewModelScope.launch {
           комикс.значение = Состояние.Загрузка()
           runCatching {getLatestComicUseCase.run()}
               .onSuccess {comic.value = State.Success(it)}
               .onFailure {comic.value = State.Error()}
       }
   }
}

И последнее — MainActivity, чтобы все подключить.

 класс MainActivity: AppCompatActivity (R.layout.activity_main) {
   private val viewModel: MainViewModel от lazy {
      ViewModelProvider(this).get(MainViewModel::class.java)
   }

   переопределить удовольствие onCreate(savedInstanceState: Bundle?) {
      super.onCreate(сохраненныйInstanceState)
      viewModel.comic.observe (это) {
         когда это) {
            это State.Loading -> {
               findViewById<TextView>(R.id.titleLabel).text = "Загрузка"
            }
            является State.Success -> {
               findViewById<TextView>(R.id.titleLabel).text = it.result.title
               Glide.with (это)
                  .load(it.result.img)
                  .в(findViewById(R.id.image))
            }
            это State.Error -> {
               findViewById<TextView>(R.id.titleLabel).text = "Ошибка"
            }
         }
      }
      viewModel.fetchComic()
   }
}

Вот и все, приложение для Android готово!

Приложение для Android, разработанное с KMM

Простой пользовательский интерфейс для iOS

Все выше было сделано в Android Studio, поэтому в этой части давайте переключимся на Xcode, чтобы было удобнее. Для этого просто откройте Xcode и выберите каталог iosApp — он содержит предварительно сконфигурированный проект Xcode. По умолчанию этот проект использует SwiftUI для GUI, так что давайте придерживаться его для простоты.

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

 перечисление Состояние {
    загрузка корпуса
    случай успеха (ComicModel)
    ошибка регистра
}

Далее давайте еще раз подготовим ViewModel

 класс ViewModel: ObservableObject {
    пусть getLatesteComicUseCase = GetLatestComicUseCase(xkcdApi: XkcdApi())
        
    @Published var comic = State.loading
        
    в этом() {
        self.comic = .загрузка
        getLatestComicUseCase.run {fetchedComic, ошибка в
            if fetchedComic != ноль {
                self.comic = .success(fetchedComic!)
            } еще {
                self.comic = .ошибка
            }
        }
    }
}

И, наконец, вид.

Примечание: для простоты я использовал компонент SwiftUI RemoteImage для отображения изображения, точно так же, как я использовал Glide на Android.

 структура ContentView: представление {
 
    @ObservedObject private (set) var viewModel: ViewModel
    
    var body: некоторый View {
        комиксВид()
    }
    --
    частная функция comicView () -> некоторый вид {
        переключить viewModel.comic {
        кейс .загрузка:
            вернуть AnyView(Текст("Загрузка"))
        case .result(пусть комикс):
            вернуть AnyView (VStack {
                Текст(комикс.название)
                RemoteImage(url: комикс.img)
            })
        случай .ошибка:
            вернуть AnyView(текст("Ошибка"))
        }
    }
}

И все, приложение для iOS тоже готово!

Приложение для iOS, разработанное с помощью KMM

Резюме

Наконец, чтобы ответить на вопрос из заголовка — мультиплатформенность Kotlin — это будущее кроссплатформенной разработки? – все зависит от потребностей. Если вы хотите создать небольшое идентичное приложение для обеих мобильных платформ одновременно, то, вероятно, нет, потому что вам нужно иметь необходимые знания по разработке для обеих платформ.

Выпуск значка продукта

Разработайте ваше следующее приложение с нашими экспертами

Получить цитату

Однако если у вас уже есть команда разработчиков Android и iOS и вы хотите обеспечить наилучшее взаимодействие с пользователем, это может значительно сократить время разработки . Как и в приведенном примере, благодаря общему модулю логика приложения была реализована только один раз, а пользовательский интерфейс был создан полностью для конкретной платформы. Так почему бы не попробовать? Как видите, начать легко.

Интересуетесь кроссплатформенной разработкой с точки зрения бизнеса? Ознакомьтесь с нашей статьей о преимуществах кроссплатформенной разработки.