Русский
Русский
English
Статистика
Реклама

Kotlin multiplatform

Варианты настройки iosMain sourceSetа в Kotlin Multiplatform Mobile

08.01.2021 16:23:02 | Автор: admin

При использовании Kotlin Multiplatform Mobile сталкиваешься с непривычной особенностьюiOS код рассматривается компилятором в нескольких вариантах: iosArm64 и iosX64, а также iosArm32 (для поддержки девайсов вышедших до iPhone 5s). При разработке под iOS на Swift про эти особенности не думаешь, так как это скрыто в headerах системных библиотек условиями препроцессора.

Для разработчика чаще всего и не должно быть необходимости учитывать архитектуру процессора, на котором будет запущено приложение (особенно если архитектуры одинаковой битности, как iosArm64 и iosX64). И код под обе архитектуры полностью одинаковый, поэтому проект настраивают под использование одного источника исходного кодаiosMain. Есть несколько вариантов объединения ios кода в одном sourceSet, каждый со своими плюсами и минусами.

Commonizer в Kotlin1.4

Kotlin Multiplatform позволяет строить иерархию из KotlinSourceSetов. Например, сделать промежуточный sourceSet со всем ios кодом, как на схеме ниже.

iosMain в иерархии (sourcehttps://kotlinlang.org/docs/reference/mpp-share-on-platforms.html)iosMain в иерархии (sourcehttps://kotlinlang.org/docs/reference/mpp-share-on-platforms.html)

С такой настройкой можно расположить весь код связанный с ios в iosMain sourceSet. Он будет успешно компилироваться, но до Kotlin 1.4 IDE не могла корректно анализировать данный код, так как не известно под какую платформу нужно делать анализArm64 или же X64. В результате мы получали ошибки в IDE (но для компилятора все было валидно):

С Kotlin 1.4 проблема поддержки IDE решена за счет нового инструментаCommonizer. Он автоматически проводит поиск общего между iosArm64Main и iosX64Main и генерирует специальную iosMain klib, в которой содержатся все общие декларации, а IDE проводит анализ используя эту klib. Подробнее про commonizer вы можете узнать в выступлении разработчика Kotlin/Native.

Для настройки своего проекта под этот вариант нужно указать в build.gradle.kts:

plugins {    kotlin("multiplatform")}kotlin {    ios {        binaries {            framework {                baseName = "shared"            }        }    }    sourceSets {        val commonMain by getting        val iosMain by getting    }}

А для включения commonizer добавляем в gradle.properties:

kotlin.mpp.enableGranularSourceSetsMetadata=truekotlin.native.enableDependencyPropagation=false

В результате получаем одно место с исходным кодом iOS и работающую помощь от IDE.

Но есть и ограниченияне всё iOS API доступно в iosMain. Например, протокол UITextFieldDelegateProtocol полностью пуст:

public expect interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {}

Хотя при работе из iosX64Main/iosArm64Main мы видим полный интерфейс:

public interface UITextFieldDelegateProtocol : platform.darwin.NSObjectProtocol {    public open fun textField(textField: platform.UIKit.UITextField, shouldChangeCharactersInRange: kotlinx.cinterop.CValue<platform.Foundation.NSRange>, replacementString: kotlin.String): kotlin.Boolean    public open fun textFieldDidBeginEditing(textField: platform.UIKit.UITextField): kotlin.Unit    ...}

А так-же при настройке cinterop (например при подключении cocoapods в Kotlin) все декларации не доступны в iosMain при просмотре через IDE (хотя для компилятора все будет корректно работать).

Настроенный пример можно посмотреть на GitHub.

Плюсы:

  1. промежуточный sourceSet полноценно поддерживается IDE

  2. отдельные gradle-задачи для компиляции обеих архитектур

Минусы:

  1. cInterop не видны для IDE в промежуточном sourceSet

  2. коммонизация работает только на 1 уровне иерархии (если за iosMain сделать appleMain для ios, macos - не будет работать обобщение)

  3. не все API доступно в промежуточном sourceSet

  4. внешние библиотеки должны иметь свой опубликованный промежуточный sourceSet (не важно как он зоветсяважно какие таргеты в нем объединены)

Один sourceSet для iOS

Следующий подход указан в документации Kotlin Multiplatform Mobile. В данном случае предлагается на этапе конфигурирования gradle выбирать какой таргет нам использоватьiosX64 или iosArm64. И выбор этот делается на основе переменной окружения SDKNAMEона подставляется Xcode автоматически. Поэтому с данным подходом мы сможем скомпилировать под девайс только из Xcode.

Настройка делается следующим образом:

import org.jetbrains.kotlin.gradle.plugin.mpp.KotlinNativeTargetplugins {    kotlin("multiplatform")}kotlin {    val iosTarget: (String, KotlinNativeTarget.() -> Unit) -> KotlinNativeTarget =        if (System.getenv("SDK_NAME")?.startsWith("iphoneos") == true)            ::iosArm64        else            ::iosX64    iosTarget("ios") {        binaries {            framework {                baseName = "shared"            }        }    }    sourceSets {        val commonMain by getting        val iosMain by getting    }}

В итоге получаем iosMain полностью работающий и с IDE и с cInterop:

Настроенный пример можно посмотреть на GitHub.

Плюсы:

  1. iosMain содержит весь код под обе платформы

  2. cInterop корректно работает

Минусы:

  1. Конфигурация в gradle зависит от переменных окружения

  2. В gradle доступна только одна задача компиляции iOS, а какая архитектура будет собираться решается переменной окружения

  3. Для компиляции под девайс нужно собирать из Xcode

Arm64 sourceSet depends onX64

Выставление зависимостей между sourceSet можно использовать и не только для иерархии. Например указать зависимость iosArm64Main от iosX64Main.

Для настройки требуется создание отдельных таргетов и указание зависимости:

plugins {    kotlin("multiplatform")}kotlin {    val ios = listOf(iosX64(), iosArm64())    configure(ios) {        binaries {            framework {                baseName = "shared"            }        }    }    sourceSets {        val commonMain by getting        val iosX64Main by getting        val iosArm64Main by getting {            dependsOn(iosX64Main)        }    }}

А весь код в таком случае располагается в директории iosX64Main:

Настроенный пример можно посмотреть на GitHub.

Плюсы:

  1. код не дублирован, лежит в одном из sourceSet

  2. всё платформенное API доступно

  3. отдельные gradle-задачи для компиляции обеих архитектур

  4. cInterop корректно поддерживается

Минусы:

  1. до Kotlin 1.4 cInterop с такой конфигурацией не поддерживался (была ошибка о подключении некорректной архитектуры в линковку)

symlink Arm64 toX64

Последний вариант, используемый нами в IceRock, позволяет не дублировать код, использовать все API и cInterop, а также не требует сложных настроек. Чтобы не дублировать код мы просто создаем symlink для одного из ios sourceSet:

ln -s iosX64Main iosArm64Main

А в gradle настраиваем проект с двумя ios таргетами:

plugins {    kotlin("multiplatform")}kotlin {    val ios = listOf(iosX64(), iosArm64())    configure(ios) {        binaries {            framework {                baseName = "shared"            }        }    }    sourceSets {        val commonMain by getting    }}

В результате получаем желаемый результат:

Настроенный пример можно посмотреть на GitHub.

Плюсы:

  1. код не дублирован, лежит в одном sourceSet, а symlink его отражает

  2. всё платформенное API доступно

  3. cinterop доступен и корректно работает на всех версиях Kotlin

Минусы:

  1. git изменения не видны при просмотре через symlink директорию

  2. IDE не замечает автоматически изменения symlink файлов (нужно делать reload directory или же просто работать всегда в одном сорссете)

  3. не работает на Windows (но для iOS и не нужно)

Подробнее..

Из JCenter в Maven или короткая заметка о публикации мультиплатформы Kotlin

06.02.2021 10:17:52 | Автор: admin

Недавно появилась новость о том, что JCenter скоро будет закрыт и как нам дальше жить там не сказано надо бы переезжать куда-то ещё. Лично для меня главным кандидатом стал MavenCentral, поскольку я уже давно там публикуюсь, хотя последний год привык делать это через Bintray. В этой заметке будет краткая информация о подводных, с которыми я встретился и как пришлось поменять свои скрипты публикции.

В ЭТОЙ СТАТЬЕ НЕТ ИНФОРМАЦИИ О ПУБЛИКАЦИИ НАТИВНХ ТАРГЕТОВ. И тем не менее, надеюсь, информация в статье будет вам полезна.

Наверно, уже писали

Действительно, есть несколько статей на эту тему (2019 года и 2021), но на данный момент я не видел ни одной полноценной статьи с информацией о скриптах публикации (наверное, просто не туда смотрел). Большую часть информации можно взять из статьи (регистрация в Sonatype, регистрация домена, создание GPG ключа и его назначение). Если вкратце, вот вам универсальный скрипт публикации мультиплатформенного проекта:

apply plugin: 'maven-publish'apply plugin: 'signing'task javadocsJar(type: Jar) { // нужно, чтобы javadocs точно были включены в публикацию    classifier = 'javadoc'}publishing {    publications.all {        artifact javadocsJar        pom {            description = "Ваше описание проекта"            name = "${project.name}"            url = "https://github.com/Owner/Project" // здесь обычная ссылка на проект, может быть и не github            scm {                developerConnection = "scm:git:[fetch=]/*ВАША ССЛКА НА .git файл*/[push=]/*Повторить предыдущую ссылку*/"                url = "/*Ещё раз повторить предыдущую ссылку*/"            }            developers {                    developer {                        id = "ID разработчика"                        name = "Имя разработчика"                        email = "email разработчика"                    }            }            licenses {                    license {                        name = "Имя лицензии"                        url = "Ссылка на LICENSE файл"                    }                            }        }                repositories {            // тут только Maven Central            maven {                name = "sonatype"                url = uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")                credentials {                    username = project.hasProperty('SONATYPE_USER') ? project.property('SONATYPE_USER') : System.getenv('SONATYPE_USER')                    password = project.hasProperty('SONATYPE_PASSWORD') ? project.property('SONATYPE_PASSWORD') : System.getenv('SONATYPE_PASSWORD')                }            }        }    }}signing {    useGpgCmd()    sign publishing.publications}

Вы можете посмотреть на пример этого скрипта тут. Во время переезде с JCenter я встретился со следующими ошибками:

  • maven url должен присваиваться именно uri("https://oss.sonatype.org/service/local/staging/deploy/maven2/")

  • Подпись должна производиться именно для publishing.publications (у меня раньше подписывась каждая публикация отдельно, отчего Java таргеты (включая Android) не получали свои подписи)

Публикация

Перед публикацией не забудьте предоставить в gradle переменные SONATYPE_USER и SONATYPE_PASSWORD. Это можно сделать несколькими способами:

  • Положить их в ~/.gradle/gradle.properties как обычные переменные

  • Экпортировать их перед публикацией в терминале (как это обычно делается в CI билдах)

Можно почитать в статьях в начале о том, как это сделать. Для публикации следует использовать следующий вызов:

$ ./gradlew --no-parallel publishAllPublicationsToSonatypeRepository

--no-parallel тут используется по той причине, что если у вас используется параллельное построение проекта (флаг --parallel или org.gradle.parallel=true в gradle.properties), то без флага отключения параллельного построения публикация будет произведена в несколько репозиторией, что может повлечь за собой невозможность закрыть и опубликовать релизы (об этом тоже писалось в статьях выше).

Вместо заключения

Публикация библиотеки - штука сложная, а потому я искренне желаю вам удачи на этом поприще. Если есть замечания/дополнения или ещё что-то, что может помочь другим - пишите в комментариях, чтобы я добавил об этом заметку в статью. Всего хорошего :)

Подробнее..

Как внедряли Kotlin Multiplatform в Профи

07.05.2021 16:08:17 | Автор: admin

Привет, Хабр! Я Миша Игнатов, тимлид в компании Профи. Моя команда отвечает за клиентские мобильные приложения на Android и iOS. Мы используем Kotlin Multiplatform в production с 2019 года. Расскажу, почему мы выбрали именно эту технологию, как внедряли её, какие ключевые этапы прошли и какие сделали выводы.

Коротко о Kotlin Multiplatform

Kotlin Multiplatform позволяет запускать один и тот же код, написанный на Kotlin, на множестве платформ. В августе 2020 года компания JetBrains представила Kotlin Multiplatform Mobile (КММ) SDK, который помогает упростить использование общего кода на Android и iOS. Цель технологии вынос бизнес-логики. UI-слой остаётся нативным, что хорошо сказывается на опыте пользователя и внешнем виде приложений.

Почему мы выбрали Kotlin Multiplatform

Мы изучали разные кросс-платформенные технологии. Например, React Native и Flutter позволяют писать сразу всё в одном проекте на обе платформы, но ограничивают разработчика языком и набором библиотек. Остановились на Kotlin Multiplatform по трём причинам.

  1. Легко интегрировать

    Общий код, написанный на Kotlin, можно внедрить с минимальными усилиями в готовое приложение. Он компилируется в привычные для платформ библиотеки. Для Android это jar или aar-библиотека, для iOS Universal Framework. Подключение и дальнейшая работа не сильно отличаются от взаимодействия с любой нативной библиотекой.

  2. Синтаксис языка Kotlin близок к Swift

    Схожесть языков снижает порог входа для iOS-разработчиков. Оба языка разделяют похожую идеологию скорость и удобство работы для разработчика. Понять, что происходит в общем коде, и дополнить его сможет любой в команде.

  3. Не нужно тратить ресурсы дважды на одну задачу

    Бизнес-логика наших приложений одинаковая. Более 70% кода не связано с платформой, на которой его запускают. Мы запрашиваем данные с сервера, преобразуем их, кешируем и готовим к отображению. Поэтому пишем код в двух проектах, дублируя логику, Android на языке Kotlin и iOS на Swift. Отличия есть только в дизайне из-за разного UX на мобильных платформах и взаимодействия с системой (запросы к различной периферии: камера, геолокация, галерея, уведомления и т.д.).

Как внедряли

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

Шаг 1. Первая строчка в общем коде

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

Обмен данными с сервером у нас реализован на GraphQL. Запрос в коде это multiline строка. Бывает пять строк, а бывает под сотню. Если отправить такой объём, бэкенду придётся тратить время на парсинг структуры. С другой стороны, нужно контролировать запрашиваемые данные во время код-ревью и валидации запросов на проде. Поэтому перед релизом мы обучаем сервер новым запросам. Это позволяет использовать хеши вместо строк.

Раньше обучение сервера мы проводили вручную отдельно для каждой платформы. Это отнимало много ресурсов и увеличивало вероятность ошибки. Например, можно забыть обучить запрос на одной из платформ и сломать приложение.

Решили вынести в общий код несколько запросов. Для этого в Android-проекте сделали мультиплатформенный модуль shared. Перенесли в него строки запросов и обернули в классы-синглтоны object, а в клиентских приложениях вызывали методы этих классов. Забавный факт использовать КММ предложил iOS-разработчик.

Первая строчка в общем коде
package ru.profi.shared.queries.client.city/*** Запрос поиска города по [Params.term]*/object GeoSelectorWarpQuery : WarpQuery<Params> {   override val hash: String? = "\$GQLID{c9d4adbb7b9ef49fc044064b9a3e662b}"   override val dirtyQuery = listOf("\$term").let { (term) ->       """       query geoSelector($term: String) {         suggestions: simpleGeoSelector(term: $term, first: 100) {           edges {             node {               name               geoCityId               regionName               hostname               countryId             }           }         }       }       """   }.trimIndent()}
Использование в Android проекте
override fun getQuery() = GeoSelectorWarpQuery.getQuery()
Использование в iOS проекте
import KotlinComponentsstruct GraphQLWarpRequests {    static let GeoSelectorWarpQuery = GeoSelectorWarpQuery()...}let model = GraphQLRequestModel(query: GraphQLWarpRequests.GeoSelectorWarpQuery.getQuery(), variables: variables)

Теперь структуры запросов лежат в одном месте. В следующем релизе общую библиотеку подключили на обеих платформах, и всё работало без проблем. Размер приложения на iOS увеличился всего на 0,8 Мб. Впоследствии вынос запросов в общий код сократил количество подходов к обучению в два раза.

Проблему ручного обучения решили утилитарной библиотекой из нескольких классов, написанных на Kotlin. Она находит в коде необученные запросы, генерирует и отправляет новые хеши через pull request в репозиторий backend. Теперь мы не тратим время на обучение, оно полностью автоматизировано.

На этом шаге мы построили инфраструктуру для общего кода на Kotlin Мultiplatform. Можно переходить к более серьёзным задачам.

Шаг 2. Создаём мультиплатформенный SDK

В один момент компания решила создать свою in-house аналитику на базе Clickhouse. Для этого на стороне backend создали API для приложений. Моей команде оставалось только отправлять события. Чтобы не мешать работе основного функционала и не терять события, если у пользователя нет сети, нужно было научиться кешировать, группировать пачки событий и отправлять их с меньшим приоритетом, чем запросы на основной функционал.

Модуль решили писать в общем коде. Для отправки событий взяли network client ktor. Для работы с сетью он нас полностью устраивал.

Когда сети нет, события надо сохранить до следующего сеанса связи. Для этого выбрали SQLDelight мультиплатформенную библиотеку для нативной базы данных.

Для асинхронных операций использовали kotlinx.coroutines. Для сериализации и десериализации выбрали kotlinx.serialization.

Чтобы повысить надёжность кода, функционал модуля покрыли unit-тестами. Удобно, что их можно запускать на разных платформах.

При интеграции приложения на Android проблем не возникло, но на iOS были падения на старте. В консоли XCode и логах Firebase Crashlytics трассировка стека не сильно приближала нас к причине. Но было ясно, что падает внутри общего кода.

Чтобы получить понятную трассировку стека, мы подключили библиотеку CrashKiOS от студии Touchlab. А при создании корутины добавили CoroutineExceptionHandler, который перехватывает исключения во время их выполнения.

Оказалось, что отправка события происходила после отмены скоупа корутины. Это и приводило к падению. Причина мы неправильно отменяли CoroutineScope в жизненном цикле приложения.

Kotlin Multiplatform позволил объединить в один модуль ответственность за отправку и хранение аналитических событий. В итоге мы построили полноценный SDK в общем коде.

Шаг 3. Переносим бизнес-логику из приложения Android в мультиплатформу

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

В приложении на iOS был такой код в модуле бизнес-логики чатов. Это была наша боль. Добавлять новый функционал становилось всё дороже код написан на Objective-C с устаревшей и сложной архитектурой. Чувствовалось, что разработчики неохотно брали задачи по чатам.

В приложении на Android бизнес-логику чатов недавно уже переписали на Kotlin. Поэтому решили попробовать вынести существующий модуль в общий код и адаптировать его под iOS.

Нам помогли ребята из IceRock.dev. Они уже давно встали на путь мультиплатформы, активно продвигают KMM и развивают сообщество. Вместе мы составили план переезда.

  1. Настроить поддержку Kotlin Multiplatform в gradle-модуле.
    Создать модуль, подключить плагины, настроить sourceSets и зависимости.

  2. Перенести платформенно-независимые классы в commonMain.
    Перенести всё, что не зависит от JVM и Android, в commonMain. Это место для общего кода, в котором нет платформенных зависимостей.

  3. Заменить библиотеки JVM/Android на мультиплатформенные аналоги.
    Перейти с org.json на kotlinx.serialization и с JodaTime на klock. Некоторые части пришлось вынести в платформозависимый код в виде expect/actual.

  4. Перенести в commonMain JVM-зависимый код, который требует изменений.
    Например, заменить JVM IOException на kotlin.Exception, а ConcurrentHashMap на использование Stately.

  5. Перенести в commonMain Android-зависимый код, который требует изменений.
    Единственной зависимостью Android SDK был компонент Service, который работает с WebSocket. Стабильного мультиплатформенного аналога на Kotlin пока нет.

    Мы решили оставить нативные реализации в приложении и подключить их через интерфейс SocketService.

    Интерфейс SocketService
    interface SocketService {    /**     * Присоединиться по сокету к [chatUrl]. Все события из сокета необходимо отдавать в [callback]     */    fun connect(chatUrl: String, callback: (SocketEvent) -> Unit)    /**     * Отсоединиться от текущего подключения по сокету.     */    fun disconnect()    /**     * Отправить сообщение [msg] в текущем подключении по сокету     */    fun send(msg: String)}
    
  6. Сделать модуль API удобным для обеих платформ.
    Так как в iOS невозможно перехватить runtime-исключения из Kotlin, мы решили обрабатывать их внутри SDK и добавить в методы интерфейса callback onError. Поэтому пришлось немного переделать интерфейс взаимодействия с клиентскими приложениями.

При переносе кода в мультиплатформу мы сформировали алгоритм миграции модулей с бизнес-логикой в общий код. Теперь пользуемся им для выноса других модулей.

План от IceRock.dev помог нам двигаться увереннее и быстрее. Мы продолжаем созваниваться и делиться опытом разработки.

Что мы поняли

Kotlin Multiplatform помог сделать единый источник правды для бизнес-логики в клиентских приложениях Профи. UI и UX оставили для пользователя нативными. При грамотном проектировании интерфейса взаимодействия с общим кодом, изменение и расширение бизнес-логики происходят в одном месте, а клиентским приложениям нужно просто поддержать это.

Мы сэкономили ресурсы. При переносе модулей в Kotlin Multiplatform мы ощутили экономию времени на разработке модуль чатов на iOS не пришлось рефакторить. Вместо этого мы перенесли решение из Android-проекта в общий код и адаптировали его для iOS. Это обошлось дешевле, чем писать чаты с нуля.

Разработчики быстро освоились. Для Android-разработчиков оказались в новинку только мультиплатформенные библиотеки и настройка build-скрипта модуля. Остальное привычно и не вызывает сложностей. iOS-разработчикам было легко понять синтаксис языка, но пришлось покопаться в сборке через Gradle. Но сейчас каждый из них уже решил как минимум одну задачу в общем коде.

Главный минус технологии время сборки для iOS. Например, когда мы искали причину падения приложения, пересобирать общий код для iOS приходилось часто. При публикации общих модулей это не ощущается. С выпусками новых версий Kotlin скорость сборки растёт, что добавляет надежд на будущий комфорт разработки.

Иногда мы спотыкались о проблемы по незнанию. Когда мы начинали, информации по внедрению KMM было очень мало, поэтому набивали шишки сами. Сейчас сообщество Kotlin Multiplatform быстро развивается. Появляется всё больше статей и докладов на конференциях и митапах. Есть каналы в Slack и Telegram, библиотеки для Kotlin Multiplatform.

Оно того стоило

Первые шаги давались сложно, так как технология была новой. Поначалу казалось проще использовать нативные решения на известных команде библиотеках, чем разобраться в новых. Но мы понимали, что дальше дело пойдёт быстрее. Так и произошло. Можно сказать, что скорость разработки в общем коде и нативных проектах сравнялась.

Сейчас у нас уже 10 общих модулей разной сложности, и мы продолжаем выносить бизнес-логику в общий код. Уверен, что Kotlin Multiplatform Mobile готов к покорению мира разработки мобильных приложений.

Подробнее..

Кроссплатформенная мобильная разработка история вопроса

04.03.2021 12:16:30 | Автор: admin

Когда речь заходит о разработке сразу для Android и iOS, начинаются холивары и гадания на кофейной гуще. Что перспективнее, Flutter или Kotlin Multiplatform? За этими технологиями будущее, или завтра их обе забудут?

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

Я не использовал лично большинство старых технологий, но благодаря общению со спикерами нашей конференции Mobius узнал многое об их опыте, и текст во многом основан на этом. Если вы работали с чем-то из перечисленного, смело дополняйте в комментариях, будет интересно и полезно.

Оглавление


Web / PWA

В 2007-м, представляя разработчикам первый iPhone, Стив Джобс объяснял, что нативные приложения не нужны: В iPhone есть полный движок Safari. Так что можете писать потрясающие Web 2.0 и Ajax-приложения, которые выглядят и действуют на iPhone именно как приложения.

Android на тот момент ещё даже не был анонсирован. Но получается, что исторически первым единым решением для iOS и Android стал веб.

Вот только разработчики тогда не разделили энтузиазма Джобса (неудивительно, учитывая тогдашнее состояние мобильного интернета). И годом позже всё-таки появился App Store для нативных приложений. А его комиссия в 30% стала новым денежным потоком для компании. В итоге её позиция сменилась на противоположную: теперь Apple считает, что правильный подход это натив (и предпочитает не вспоминать, что там её лидер говорил в 2007-м, Океания всегда воевала с Остазией).

Однако идея веб-приложений не исчезла, а продолжила развиваться. И в 2015-м новое поколение таких приложений назвали Progressive Web Apps. Они могут хранить данные локально, работать в офлайне, а ещё их можно установить на домашний экран смартфона. Чем это тогда не настоящие мобильные приложения? Что ещё для счастья надо?

Ну, например, для счастья нужны push-уведомления. По состоянию на 2021-й iOS не поддерживает их у PWA, и это мощнейший стопор для распространения подхода. Получается, компания, которая первой хвалила веб-приложения, позже сама поставила главное препятствие на их пути. На change.org есть даже петиция, обращённая к Apple: мы всё понимаем, вы дорожите своими 30%, но эта ситуация должна измениться.

Что в итоге: подход жив, но не стал общепринятым во многом из-за ограничений Apple. В будущем что-то может измениться, стоит следить.


PhoneGap/Apache Cordova

Это решение тоже связано с вебом, но подход другой так называемый гибридный. Тут предложили использовать знакомую троицу HTML/CSS/JS, но результат не публиковать в виде сайта, а упаковать в контейнер нативного приложения. В таком случае не сталкиваешься с ограничениями веба и можешь реализовать те же push-уведомления.

PhoneGap появился в 2009-м благодаря компании Nitobi, а в 2011-м Adobe купила её и создала также опенсорсный вариант Apache Cordova. У проекта модульная архитектура, позволяющая подключать плагины. И в сочетании с опенсорсностью это означает, что если Cordova не умеет чего-то нужного (например, взаимодействовать с акселерометром смартфона), сообщество может научить. Вроде как прекрасно, да?

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

Почему так? Среди главных причин называют UX, уступающий нативным приложениям. Всё хуже и с производительностью, и с плавностью. Но основное отличие даже не измерить числами: пользователю попросту заметна разница между гибридным приложением и нативным, они производят разное впечатление. Для людей через WebView ощущения не те.

А с годами сказалось ещё и развитие веба. Чем лучше становились веб-приложения и чем быстрее становился мобильный интернет, тем меньше преимуществ можно было получить от гибридного подхода.

Интересно, что авторы проекта сами предвидели такое развитие событий и ещё в 2012-м написали, что итоговая цель PhoneGap прекратить своё существование. И недавно эта цель была достигнута: в 2020-м Adobe заявили о прекращении разработки, ссылаясь на то, что нишу закрыли PWA.

Что в итоге: разработка прекращена.


Qt

Проект Qt помогал людям заниматься кроссплатформенной разработкой ещё с 90-х, когда речь шла о десктопных ОС, а не мобильных. При этом он завязан на C++, который для Android и iOS не совсем чужой: даже нативные разработчики на этих платформах могут обращаться к плюсам. Так что, казалось бы, поддержка iOS/Android со стороны Qt просто напрашивалась.

Но поначалу дело осложнялось тем, что в 2008-м проект купила Nokia. Компания тогда делала ставку на Symbian и не горела желанием помогать конкурентам. В 2010-м возможностью запускать Qt-приложения на Android занимались энтузиасты, и на Хабре об этом писали:

В 2011-м Nokia отказалась от Symbian в пользу Windows Phone, а часть проекта Qt продала компании Digia. И тогда началась работа над поддержкой одновременно Windows 8, Android и iOS. Ну вот теперь-то счастье? Спустя 10 лет ясно, что тоже нет.

Вакансии по мобильной разработке на Qt сейчас единичные. Хабрапосты о ней появляются очень редко и не свидетельствуют об успехе: в одном причиной использования Qt стала ОС Аврора (экс-Sailfish), в другом попросту у меня уже был большой опыт с ним.

Что помешало? Я встречал жалобы на то, что недостаточно было использовать сам Qt и писать на С++/QML. Потому что средствами Qt многое было не реализовать, и приходилось-таки иметь дело с конкретной платформой (например, на Android работать с Java, увязывая это с плюсовой частью через JNI). Всё это очень неудобно и подрывает исходную идею бодренько запилим два приложения по цене одного.

При этом здесь пользователь тоже ощущает не-нативность. А к IDE Qt Creator есть нарекания, рантайм Qt увеличивает размер приложения, бесплатный вариант Qt подходит не всегда и может понадобиться недешёвый коммерческий. Кроме того, мне лично кажется, что ещё сказался язык. Кроссплатформенной разработке желательно быть такой, чтобы нативным разработчикам было попроще перекатиться туда, а с языком C++ слово попроще не ассоциируется.

И хотя случаи использования Qt встречаются до сих пор, это скорее исключения из правил, у которых могут быть какие-то особые исходные условия: например, хотим перетащить на телефоны с десктопа уже имеющуюся кодовую базу на C++.

Что в итоге: крайне нишевое решение, которое не умерло полностью, но используется очень узкой прослойкой.


Xamarin

Мигель де Икаса ещё в проекте Mono занимался тем, что притаскивал .NET на несвойственные ему платформы (начав с Linux). А когда в 2011-м он вместе со всей командой Mono попал под сокращение, основал новую компанию Xamarin, собрал прежних коллег там, и сосредоточился на мобильных платформах: мол, давайте писать мобильные приложения для них на C#, вот инструмент для этого.

Надо понимать контекст того времени. Годом ранее компания Microsoft выпустила Windows Phone и стремилась стать третьим большим игроком на мобильном рынке. И даже большая Windows лезла на мобильный рынок: готовилась к выходу Windows 8, оптимизированная для планшетов.

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

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

Ещё порой вызывали недовольство и то, что после появления новых фич в Android/iOS приходилось ждать их поддержки в Xamarin, и стоимость лицензии, и общее состояние экосистемы (документация, поддержка, библиотеки).

А тем временем компания Microsoft возлюбила опенсорс и сторонние платформы вроде Linux, так что идеи де Икасы оказались ей близки, и в итоге она купила Xamarin. Теперь его наработки вошли в .NET 5, и в прошлом году представили .NET MAUI (Multi-platform App UI) развитие тулкита Xamarin.Forms. В общем, не забросили купленное.

Что в итоге: пока всё остаётся в странноватом статусе, когда и масштабного взлёта не получилось, и полностью не прекращено.


React Native

Наконец, самое свежее. В 2013-м в Facebook выпустил React, ставший очень успешным во фронтенде, а в 2015-м за ним последовал React Native, приносящий подход в мобильную разработку.

Писать предлагается на JavaScript, поэтому кому-то может показаться, что это реинкарнация PhoneGap и его HTML-подхода, но нет. Когда-то Facebook действительно полагался для мобильных устройств на HTML, но ещё в 2012-м Цукерберг назвал это ошибкой. И у React Native идея не в том, чтобы с помощью HTML/CSS городить что хочешь, а в том, чтобы с помощью JSX использовать нативные элементы UI так что пользователь ощущал себя прямо как с нативным приложением.

Насколько это получилось? Шероховатости и срезанные углы существуют с ними сталкиваются и разработчики, и пользователи. Для кого-то они оказываются критичными: особенно нашумел пост Airbnb, где после React Native решили вернуться к нативной разработке. Но какие-то другие компании (вроде самой Facebook) эти ограничения не остановили, использование в индустрии есть.

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

Если зайти на HeadHunter и сравнить число вакансий по React Native с нативной разработкой, то их немного. Но вот если сравнивать с Qt/Xamarin/PhoneGap то вакансий окажется больше, чем у них всех, вместе взятых, это наиболее успешный вариант.

Что в итоге: React Native не стал так успешен, как его фронтендовый старший брат, но определённую нишу занял.


Выводы

Обычно в холиварах занимают крайние позиции: эта крутая штука всех победит или все эти глупости скоро отомрут. А у меня по итогам всех вариантов позиция получается сдержанной:

  • Победит и умрёт не единственные варианты. Есть ситуации, когда технология занимает небольшой сегмент рынка и не претендует на мировое господство, но приносит кому-то пользу.

  • Кроссплатформа это всегда компромисс. Выдержать планку качества, заданную нативом, не получалось нигде. Везде что-то вытарчивает и пользователи могут заметить разницу, и разработчикам приходится возиться со сложностями. И далеко не для всего кроссплатформа годится одинаково хорошо: если бизнес-логика между платформами шарится хорошо, то с UI всё сложнее.

  • Но качество ниже нативного не означает, что кроссплатформа плоха и не нужна. Кому-то на этот компромисс вполне стоит идти (например, стартапу, где вся жизнь один сплошной компромисс). В каждом проекте надо взвешивать свои за и против, и результаты в разных случаях будут различаться.

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

А вот про Kotlin Multiplatform сделать выводы по прошлому у меня не получилось, потому что у него нетипичная ситуация, отличающаяся от предшественников. Во-первых, идея кроссплатформа годится не для всего тут заложена прямо в фундамент: а мы и не предлагаем объединять всё, реализуйте общую бизнес-логику, а остальное на Android и iOS делайте раздельно. А во-вторых, тут играет на руку родной для Android язык: он уже знаком половине мобильных разработчиков, и такого в кроссплатформе раньше не возникало. Так что опыт предыдущих технологий тут непоказателен остаётся смотреть, что покажет время.

На последнем Mobius было сразу несколько кроссплатформенных докладов (три про Flutter, один про Kotlin Multiplatform) мы уже выложили все видеозаписи конференции на YouTube, так что можете сами их посмотреть. А мы тем временем вовсю работаем над следующим Mobius: он пройдёт 13-16 апреля в онлайне, и там без освещения кроссплатформы тоже не обойдётся. Так что, если вас заинтересовал этот пост, то вам будет интересно и там.

Подробнее..

Как Kotlin Multiplatform экономит время на разработку. Личный опыт создания игрового приложения для KotlinConf 2019

05.11.2020 10:06:30 | Автор: admin

Привет! На связи IceRock Development, команда из Новосибирска.


Хотим поделиться полезным опытом. Вот уже 2 года мы занимаемся разработкой на Kotlin Multiplatform. В 2018 году начали реализовывать свои проекты и сразу сделали несколько открытий. В том числе выяснили, что мультиплатформенная технология значительно экономит силы и время.




Мы еще раз убедились в ее эффективности на KotlinConf 2019, которая проходила в Копенгагене. Это самое масштабное событие в мире программистов на Kotlin. Они собираются, чтобы провести воркшопы, обменяться опытом и просто приятно провести время в кругу единомышленников.



Да, многолюдненько. Потому что и сообщество программистов объединяет несколько десятков стран. Фото с официального сайта JetBrains


JetBrains, организаторы конференции и создатели языка Kotlin, предложили нам проект. На конференции представляли новую версию языка. Она позволяет писать код в том числе под смарт-часы Apple. Чтобы продемонстрировать эту возможность, мы должны были создать приложение под три платформы: iOS, Android и WatchOS.


Квест во имя Kotlin: что получилось сделать для конференции


Во время подготовки к KotlinConf мы вместе с JetBrains работали над Kotlin Locator/Finder. Это игра-квест, где нужно было побегать по Bella Center. К слову, это второй по величине конференц-центр в Скандинавии, который специально арендовали для KotlinConf 2019 в Копенгагене! Здесь собрались 1700 разработчиков со всего мира.


Суть такая: скачиваешь приложение и с помощью подсказок начинаешь искать волшебные точки, которые помечены iBeacon-метками. В этих точках можно узнать интересные подробности про Kotlin: историю языка, новые инструменты. Точку обнаружить не так-то просто: она появляется, только если приближаешься к ней на определенное расстояние.


Чтобы пройти игру, нужно найти 6 маячков в разных местах.



Таким получился интерфейс Kotlin Locator/Finder на iOS для смартфона


JetBrains создали сервер-приложение с несколькими запросами на базе Kotlin Spinner Game. Нам позвонили, рассказали про идею. Арт-лид стал колдовать над дизайном, а технический директор со специалистом занялись начинкой приложения.


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



Так выглядит интерфейс Kotlin Locator/Finder приложения для смарт-часов


Все протестировали. В самом конце подключился Android-программист, реализовал только UI для Android. И вуаля! приложение готово.


Делимся наработками:



В процессе у нас получилось:


  • первый раз использовать Kotlin/Native на WatchOS с версией компилятора 1.3.60 всё прошло отлично!
  • отладить взаимодействие из общего kotlin кода с iBeacon-метками. Мы работали с библиотекой Reedyuk/blue-falcon. Библиотеку в итоге форкнули себе и доработали.

    Часть изменений зареквестили автору оригинала: поиск устройств, отображение устройств. Он принял обратную связь и поблагодарил.


Итог: можно сказать, удалось все, что запланировали. Заодно потестили новую верисю Kotlin Multiplatform и убедились, что на ней действительно можно писать приложение под часы.

Само приложение скачали несколько сотен раз в Play Market и в App Store. Для локального мероприятия очень даже себе показатель. До финала квеста дошли 20 человек. Победителей JetBrains отметили призами.



Только посмотрите на эти лица! Да, слегка смущенно выглядят ребята



Еще бы! Потому что на них смотрят самые крутые Kotlin-разработчики мира!

Сэкономили время и затраты на команду: что нам дала мультиплатформенная технология


Над приложением фулл-тайм работал барабанная дробь! только iOS-разработчик. Остальные: дизайнер, Android-программист и тестировщик трудились на неполную ставку. И сделали полностью нативное приложение!


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


С Kotlin и скорость разработки стала значительно выше. Мы использовали moko-network и по openapi-спецификации сгенерировали весь код по работе сервером для двух платформ. Также весь код логики: логику самой игры, работу с данными, локальное хранение и пр. написали один раз на Kotlin. Ничего не пришлось дублировать.


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


Код Kotlin Multiplatform очень похож на Swift. У приложения Kotlin Locator/Finder на iOS и Android общая бизнес-логика: за поведение на обеих платформах отвечает один кусок кода. Это дает одинаковую детализацию и стабильность работы. Соответственно, и комфорт для пользователя.



Приложение на iOS



Приложение на Android


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


Что может дать Kotlin Multiplatform любому бизнесу


Совместное с JetBrains приложение мы делали скорее как имиджевый проект. Но разработка тоже бизнес. Мы побывали в роли своих же клиентов: точно так же тратили ресурсы и ждали результата, как обычно делают при заказе приложения для коммерческих целей. Поэтому в том, как эффективно Kotlin Multiplatform помогает решить бизнес-задачи, убедились на личном опыте.


Резюмируем кратко:


Быстрый выход на рынок. Все этапы разработки, проверки и тестирования проходят почти в 1,5 раза быстрее, чем при создании нативных приложений отдельно на iOS и Android. Хотя все зависит от сложности UI. Так что ускорить разработку в среднем можно на 2050%.


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


Если проект сложнее, одному спецу нужно написать общий код и бизнес-логику. Разработчику другой платформы не придется делать двойную работу. Останется подключить приложение к общей библиотеке и добавить UI.


Полностью нативное приложение. То есть адаптированное под конкретную платформу. Удобное как для разработчика, так и для пользователя.


Совместимость с другими языками программирования. Если у вас есть приложение, в которое вы хотите интегрировать Kotlin Multiplatform, его не придется полностью переписывать. Язык похож на Swift и совместим с Java.


Сейчас мы в IceRock Development ведем большинство проектов с помощью этой технологии. 2019 год мы в компании объявили годом Kotlin Multiplatform.


Стараемся внести свой вклад и в развитие международного Kotlin-сообщества. Подскажем пару полезностей:


  • https://moko.icerock.dev наши опенсорс библиотеки для быстрого старта при использовании Kotlin Multiplatform;
  • https://libs.kmp.icerock.dev сайт с библиотеками для Kotlin Multiplatform;
  • https://kmp.icerock.dev сайт с библиотеками и ссылками на проекты;
  • https://t.me/kotlinmpp телеграм-канал, где 800+ программистов обсуждают нюансы использования технологии.

Если есть вопросы, напишите нам на mpp@icerock.dev. Рады любой обратной связи!

Подробнее..

Перевод Студийные приложения Netflix на Android и iOS теперь с Kotlin Multiplatform

09.11.2020 20:17:29 | Автор: admin
Примечание от переводчика: при словах мобильные приложения Netflix вспоминаются их основные приложения для просмотра видеоконтента, но компания ведёт и киностудийную разработку для тех, кто этот контент создаёт. И их пост посвящён использованию Kotlin Multiplatform во втором случае.

В последние годы Netflix разрабатывает сервис Prodicle для индустрии производства телесериалов и фильмов. Мир съёмок живёт на высоких скоростях, а его запросы существенно различаются в разных странах, регионах и даже проектах. Его специфика означает, что мы делаем софт с упором на запись, в распределённом окружении, причём у устройств с ним на съёмочной площадке менее чем в трети всех случаев интернет-соединение будет очень надёжным, а право на ошибку у нас ограничено. Поэтому мы, будучи маленькой инженерной командой, обнаружили, что оптимизация надёжности и скорости выпуска продукта просто необходима нам, чтобы успешно отвечать растущим запросам наших клиентов.


Поскольку сетевое соединение часто оказывается ненадёжным, мы обратились к мобильным решениям для персистентности на клиентской стороне и поддержки офлайна. А потребность выпускать быстро привела к экспериментам с мультиплатформенной архитектурой. И теперь мы зашли тут ещё на шаг дальше, использовав Kotlin Multiplatform, чтобы писать платформонезависимую бизнес-логику один раз на Kotlin и компилировать её в Kotlin-библиотеку для Android и нативный Universal Framework для iOS с помощью Kotlin/Native.



Kotlin Multiplatform


Kotlin Multiplatform позволяет вам делать единую кодовую базу для бизнес-логики iOS- и Android-приложений. Вам требуется писать код для конкретной платформы только там, где это необходимо: например, для реализации нативного UI или при работе с платформоспецифичными API.

Kotlin Multiplatform подходит к кроссплатформенной мобильной разработке не так, как некоторые другие известные технологии. В то время как другие полностью абстрагируются от платформозависимой разработки, Kotlin Multiplatform дополняет её, он нацелен на замену только платформо-агностичной бизнес-логики. Он даёт новый инструмент в ваш набор, а не выкидывает весь набор инструментов и заменяет на другой.


Этот подход хорошо работает для нас по нескольким причинам:


  1. У наших приложений для Android и iOS общая архитектура со схожей, а порой и идентичной бизнес-логикой на обеих платформах.
  2. Почти 50% нашего продакшн-кода в наших Android- и iOS-приложениях не связано с платформой.
  3. Это никак не мешает нам изучать новые технологии от самих этих платформ (Jetpack Compose, Swift UI и так далее).

Итак, что мы с этим делаем?


Управление опытом (experience management)


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


Отделить код, который управляет этими конфигурациями, от самих приложений помогает уменьшать сложность при росте приложений. И наш первый эксперимент с единым кодом для разных платформ затронул реализацию мобильного SDK для нашего внутреннего инструмента Hendrix, который отвечает как раз за это управление опытом.


По своей сути Hendrix это простой интерпретируемый язык, который выражает, как должны вычисляться значения конфигурации. Эти выражения оцениваются в контексте текущей сессии приложения, и могут обращаться к данным вроде местонахождения устройства, его атрибутам, значениям A/B-тестов. В нашем случае мы конфигурируем наборы функций приложения, зависящие от региона, версии и типа проекта.


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


Это привело нас к решению сделать легковесный мобильный SDK для Hendrix и он был отличным кандидатом для Kotlin Multiplatform, так как требует значимой бизнес-логики и полностью платформонезависим.


Реализация


Для краткости мы опустим конкретные детали о Hendrix и затронем отличия в использовании Kotlin Multiplatform от Kotlin/Swift.


Сборка


На Android всё как обычно. Hendrix Multiplaform SDK подключается с помощью Gradle в качестве Android-библиотеки как любая другая зависимость. В случае с iOS нативный бинарь включается в проект Xcode как универсальный фреймворк.


Эргономика разработки


В случае с Kotlin Multiplatorm исходный код можно редактировать, перекомпилировать и добавлять к нему отладчик с брейкпойнтами хоть в Android Studio, хоть в Xcode (включая поддержку lldb). Android Studio работает из коробки, поддержка Xcode достигается с помощью плагина xcode-kotlin от TouchLabs.



Отлаживаем Kotlin-исходники в Xcode


Работа с сетью


Hendrix интерпретирует набор правил удалённо конфигурируемые файлы, которые оказываются скачаны на устройство. Мы используем Multiplatform HttpClient из фреймворка Ktor, чтобы добавить наш код работы с сетью в SDK.


Дисковый кэш


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


Завершающие мысли


Мы внимательно следим за эволюцией Kotlin Multiplatform в последние годы и верим, что технологиях достигла точки перегиба. Тулинг и интеграции с системой сборки для Xcode улучшились достаточно, и теперь сложности, связанные с интеграцией и обслуживанием, больше не перевешивают преимущества единой имплементации для разных платформ.


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


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


Мы обратили внимание на пост Netflix, потому что перекликающийся с ним доклад скоро представят на нашей конференции Mobius: там тоже расскажут об опыте внедрения Kotlin Multiplatform в продакшн крупной компанией. Только речь пойдёт не о малоизвестном нишевом приложении, а о суперпопулярных Яндекс.Картах. Если два таких гиганта почти одновременно заговорили о продакшн-опыте, значит ли это, что вот теперь время Kotlin Multiplatform пришло?
Подробнее..

Kotlin Multiplatform. Работаем с многопоточностью на практике. Ч.2

19.12.2020 22:21:31 | Автор: admin
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании Usetech.
В предыдущей статье я рассказывала про один из способов реализации многопоточности в приложении Kotlin Multiplatform. Сегодня мы рассмотрим альтернативную ситуацию, когда мы реализуем приложение с максимально расшариваемым общим кодом, перенося всю работу с потоками в общую логику.
В прошлом примере нам помогла библиотека Ktor, которая взяла на себя всю основную работу по обеспечению асинхронности в сетевом клиенте. Это избавило нас от необходимости использовать DispatchQueue на iOS в том конкретном случае, но в других нам бы пришлось использовать задание очереди исполнения для вызова бизнес-логики и обработки ответа. На стороне Android мы использовали MainScope для вызова suspended функции.

Итак, если мы хотим реализовать единообразную работу с многопоточностью в общем проекте, то нам потребуется корректно настроить scope и контекст корутины, в котором она будет выполняться.
Начнем с простого. Создадим нашего архитектурного посредника, который будет вызывать методы сервиса в своем scope, получаемом из контекста корутины:
class PresenterCoroutineScope(context: CoroutineContext) : CoroutineScope {    private var onViewDetachJob = Job()    override val coroutineContext: CoroutineContext = context + onViewDetachJob    fun viewDetached() {        onViewDetachJob.cancel()    }}//Базовый класс для посредникаabstract class BasePresenter(private val coroutineContext: CoroutineContext) {    protected var view: T? = null    protected lateinit var scope: PresenterCoroutineScope    fun attachView(view: T) {        scope = PresenterCoroutineScope(coroutineContext)        this.view = view        onViewAttached(view)    }}

Вызываем сервис в методе посредника и передаем нашему UI:
class MoviesPresenter:BasePresenter(defaultDispatcher){    var view: IMoviesListView? = null    fun loadData() {        //запускаем в скоупе        scope.launch {            service.getMoviesList{                val result = it                if (result.errorResponse == null) {                    data = arrayListOf()                    data.addAll(result.content?.articles ?: arrayListOf())                    withContext(uiDispatcher){                    view?.setupItems(data)                   }                }            }        }//IMoviesListView - интерфейс/протокол, который будут реализовывать UIViewController и Activity. interface IMoviesListView  {  fun setupItems(items: List<MovieItem>)}class MoviesVC: UIViewController, IMoviesListView {private lazy var presenter: IMoviesPresenter? = {       let presenter = MoviesPresenter()        presenter.attachView(view: self)        return presenter    }()    override func viewWillAppear(_ animated: Bool) {        super.viewWillAppear(animated)        presenter?.attachView(view: self)        self.loadMovies()    }    func loadMovies() {        self.presenter?.loadMovies()    }   func setupItems(items: List<MovieItem>){}//....class MainActivity : AppCompatActivity(), IMoviesListView {    val presenter: IMoviesPresenter = MoviesPresenter()    override fun onResume() {        super.onResume()        presenter.attachView(this)        presenter.loadMovies()    }   fun  setupItems(items: List<MovieItem>){}//...


Чтобы корректно создавать scope из контекста корутины, нам потребуется задать диспетчер корутины.
Это платформозависимая логика, поэтому используем кастомизацию с помощью expect/actual.
expect val defaultDispatcher: CoroutineContextexpect val uiDispatcher: CoroutineContext

uiDispatcher будет отвечать за работу в потоке UI. defaultDispatcher будем использовать для работы вне UI потока.
Проще всего создать в androidMain, т.к в Kotlin JVM есть готовые реализации для диспетчеров корутин. Для доступа к соответствующим потокам используем CoroutineDispatchers Main (UI поток) и Default (стандартный для Coroutine):
actual val uiDispatcher: CoroutineContext    get() = Dispatchers.Mainactual val defaultDispatcher: CoroutineContext    get() = Dispatchers.Default


Диспетчер MainDispatcher выбирается для платформы под капотом CoroutineDispatcher с помощью фабрики диспетчеров MainDispatcherLoader:

internal object MainDispatcherLoader {    private val FAST_SERVICE_LOADER_ENABLED = systemProp(FAST_SERVICE_LOADER_PROPERTY_NAME, true)    @JvmField    val dispatcher: MainCoroutineDispatcher = loadMainDispatcher()    private fun loadMainDispatcher(): MainCoroutineDispatcher {        return try {            val factories = if (FAST_SERVICE_LOADER_ENABLED) {                FastServiceLoader.loadMainDispatcherFactory()            } else {                // We are explicitly using the                // `ServiceLoader.load(MyClass::class.java, MyClass::class.java.classLoader).iterator()`                // form of the ServiceLoader call to enable R8 optimization when compiled on Android.                ServiceLoader.load(                        MainDispatcherFactory::class.java,                        MainDispatcherFactory::class.java.classLoader                ).iterator().asSequence().toList()            }            @Suppress("ConstantConditionIf")            factories.maxBy { it.loadPriority }?.tryCreateDispatcher(factories)                ?: createMissingDispatcher()        } catch (e: Throwable) {            // Service loader can throw an exception as well            createMissingDispatcher(e)        }    }}


Так же и с Default:
internal object DefaultScheduler : ExperimentalCoroutineDispatcher() {    val IO: CoroutineDispatcher = LimitingDispatcher(        this,        systemProp(IO_PARALLELISM_PROPERTY_NAME, 64.coerceAtLeast(AVAILABLE_PROCESSORS)),        "Dispatchers.IO",        TASK_PROBABLY_BLOCKING    )    override fun close() {        throw UnsupportedOperationException("$DEFAULT_DISPATCHER_NAME cannot be closed")    }    override fun toString(): String = DEFAULT_DISPATCHER_NAME    @InternalCoroutinesApi    @Suppress("UNUSED")    public fun toDebugString(): String = super.toString()}


Однако, не для всех платформ есть реализации диспетчеров корутин. Например, для iOS, который работает с Kotlin/Native, а не с Kotlin/JVM.
Если мы попробуем использовать код, как в Android, то получим ошибку:


Давайте разберем, в чем же у нас дело.

Issue 470 c GitHub Kotlin Coroutines содержит информацию, что специальные диспетчеры еще не реализованы для iOS:


Issue 462, от которой зависит 470, то же еще в статусе Open:


Рекомендуемым решением является создание собственных диспетчеров для iOS:
actual val defaultDispatcher: CoroutineContextget() = IODispatcheractual val uiDispatcher: CoroutineContextget() = MainDispatcherprivate object MainDispatcher: CoroutineDispatcher(){    override fun dispatch(context: CoroutineContext, block: Runnable) {        dispatch_async(dispatch_get_main_queue()) {            try {                block.run()            }catch (err: Throwable) {                throw err            }        }    }}private object IODispatcher: CoroutineDispatcher(){    override fun dispatch(context: CoroutineContext, block: Runnable) {        dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.toLong(),0.toULong())) {            try {                block.run()            }catch (err: Throwable) {                throw err            }        }    }


При запуске мы получим ту же самую ошибку.
Во-первых, мы не можем использовать dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT.toLong(),0.toULong())), потому что он не привязан ни к одному потоку в Kotlin/Native:

Во-вторых, Kotlin/Native в отличие от Kotlin/JVM не может шарить корутины между потоками. А также любые изменяемые объекты.
Поэтому мы используем MainDispatcher в обоих случаях:
actual val ioDispatcher: CoroutineContextget() = MainDispatcheractual val uiDispatcher: CoroutineContextget() = MainDispatcher@ThreadLocalprivate object MainDispatcher: CoroutineDispatcher(){    override fun dispatch(context: CoroutineContext, block: Runnable) {        dispatch_async(dispatch_get_main_queue()) {            try {                block.run().freeze()            }catch (err: Throwable) {                throw err            }        }    }


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

Однако, если мы попытаемся заморозить уже замороженный объект, например, синглтоны, которые считаются замороженными по умолчанию, то получим FreezingException.
Чтобы этого не произошло, помечаем синглтоны аннотацией @ThreadLocal, а глобальные переменные @SharedImmutable:
/** * Marks a top level property with a backing field or an object as thread local. * The object remains mutable and it is possible to change its state, * but every thread will have a distinct copy of this object, * so changes in one thread are not reflected in another. * * The annotation has effect only in Kotlin/Native platform. * * PLEASE NOTE THAT THIS ANNOTATION MAY GO AWAY IN UPCOMING RELEASES. */@Target(AnnotationTarget.PROPERTY, AnnotationTarget.CLASS)@Retention(AnnotationRetention.BINARY)public actual annotation class ThreadLocal/** * Marks a top level property with a backing field as immutable. * It is possible to share the value of such property between multiple threads, but it becomes deeply frozen, * so no changes can be made to its state or the state of objects it refers to. * * The annotation has effect only in Kotlin/Native platform. * * PLEASE NOTE THAT THIS ANNOTATION MAY GO AWAY IN UPCOMING RELEASES. */@Target(AnnotationTarget.PROPERTY)@Retention(AnnotationRetention.BINARY)public actual annotation class SharedImmutable


В итоге, внеся все правки, мы получаем работающее одинаково на обеих платформах приложение:


Исходники примера github.com/anioutkazharkova/movies_kmp
tproger.ru/articles/creating-an-app-for-kotlin-multiplatform
github.com/JetBrains/kotlin-native
github.com/JetBrains/kotlin-native/blob/master/IMMUTABILITY.md
github.com/Kotlin/kotlinx.coroutines/issues/462
helw.net/2020/04/16/multithreading-in-kotlin-multiplatform-apps

Подробнее..

Kotlin Multiplatform. Работаем с многопоточностью на практике. Ч.1

19.12.2020 22:21:31 | Автор: admin
Доброго всем времени суток! С вами я, Анна Жаркова, ведущий мобильный разработчик компании Usetech
Я давно занимаюсь не только нативной разработкой (как iOS, так и Android), но и кросс-платформенной. В свое время я очень плотно писала на Xamarin (iOS, Android, так и Forms). Так как я интересуюсь различными технологиями шаринга кода, то не прошла и мимо Kotlin Multiplatform (KMM). И сегодня мы с вами поговорим об этом SDK, и как с ним работать на практике.
В сети хватает базовых примеров приложений на KMM, поэтому мы рассмотрим что-то, более приближенное к нашим ежедневным разработческим задачам, а именно, как реализовать многопоточное приложение на Kotlin Multiplatform.



Для начала немного вводной информации. Если вы уже знакомы с Kotlin Multiplatform, то листайте ниже до примера.

Основная идея KMM, как и других кросс-платформенных технологий оптимизация разработки путем написания кода один раз и последующего его использования на разных платформах.

Согласно концепции JetBrains, Kotlin Multiplatform не является фреймворком. Это именно SDK, который позволяет создавать модули с общим кодом, подключаемые к нативным приложениям.


Для взаимодействия с платформами используются специфические для платформы версии Kotlin: Kotlin/JVM, Kotlin/JS, Kotlin/Native. Данные версии включают расширения языка Kotlin, а также специфичные для конкретной платформы библиотеки и инструменты. Написанный на Kotlin модуль компилируется в JVM байткод для Android и LLVM байткод для iOS.


Модуль (Shared, Common) содержит переиспользуемую бизнес-логику. Платформенные модули iOS/Android, к которым подключен Shared/Common, либо используют написанную логику напрямую, либо имплементируют свою реализацию в зависимости от особенностей платформы.

Общая бизнес-логика может включать в себя:
  • сервисы для работы с сетью;
  • сервисы для работы с БД;
  • модели данных.


Также в нее могут входить архитектурные компоненты приложения, напрямую не включающие UI, но с ним взаимодействующие:
  • ViewModel;
  • Presenter;
  • Интеракторы и т.п.

Концепцию Kotlin Multiplatform можно сравнить с реализацией Xamarin Native. Однако, здесь нет модулей или функционала, реализующих UI. Эта логическая нагрузка ложится на подключенные нативные проекты.

Теперь рассмотрим подход на практике.
Если вы еще не работали с KMM, то потребуется установить и настроить инструменты. Раньше это было довольно хлопотно, но сейчас достаточно установить Android Studio (версии от 4.1) и плагин Kotlin Multiplatform Mobile . Выбираем шаблон KMM Application при создании проекта, и все отработает автоматически.


Мультиплатформенные проекты Kotlin обычно делятся на несколько модулей:
  • модуль переиспользуемой бизнес-логики (Shared, commonMain и т.п);
  • модуль для IOS приложения (iOSMain, iOSTest);
  • модуль для Android приложения (androidMain, androidTest).

В них располагается наша бизнес-логика. Всю используемую в проекте бизнес-логику можно разделить на:
  • переиспользуемую (общую);
  • платформенную реализацию.

Переиспользуемая логика располагается в проекте commonMain в каталоге kotlin и разделяется на package. Декларации функций, классов и объектов, обязательных к переопределению, помечаются модификатором expect:

Реализации должны иметь модификатор actual.
В качестве примера работы с многопоточностью рассмотрим небольшое приложение, обращающееся к стороннему API по сети:

Я выбрала www.themoviedb.org. Полный код примера будет по ссылке внизу статьи.

В общей Common части расположим общую бизнес-логику:

А именно наш сетевой сервис. Это логично.
В модулях iOS/Android приложений оставим только UI компоненты для отображения списка и адаптеры. iOS часть будет написана на Swift, Android на Kotlin.

Начнем с бизнес-логики. Т.к весь функционал будет в модуле common, то мы будем использовать в качестве библиотек решения для Kotlin Multiplatform:

Ktor библиотека для работы с сетью и сериализации.

В build.gradle (:app) пропишем следующие зависимости:
val ktorVersion = "1.4.0"val serializationVersion = "1.0.0-RC" sourceSets {        val commonMain by getting {            dependencies {                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")                implementation("io.ktor:ktor-client-core:$ktorVersion")                implementation("org.jetbrains.kotlinx:kotlinx-serialization-core:$serializationVersion")                implementation("io.ktor:ktor-client-serialization:$ktorVersion")            }        }        val androidMain by getting {            dependencies {                //...                implementation("io.ktor:ktor-client-android:$ktorVersion")            }        }        val iosMain by getting {            dependencies {                implementation("io.ktor:ktor-client-ios:$ktorVersion")            }        }        ...


Также добавим поддержку сериализации:
plugins {    //...    kotlin("plugin.serialization") version "1.4.10"}


Далее нам надо определить, что делать с многопоточностью, ведь она реализуется по-разному на каждой платформе. На стороне iOS мы используем GCD (Grand Central Dispatch), а на стороне Android JVM Threads и Coroutines. Однако, в Kotlin Multiplatform мы можем сделать общей и работу с многопоточностью.
Для этого мы будет использовать Kotlin Coroutines:
val coroutinesVersion = "1.3.9-native-mt" sourceSets {        val commonMain by getting {            dependencies {                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:$coroutinesVersion")               //...            }        }        val androidMain by getting {            dependencies {                implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion")               //...            }        }        val iosMain by getting {            dependencies {                //...            }        }        ...

Тут стоит сделать пояснение, как с этим работать, потому что далеко не все iOS разработчики знают, что такое Coroutines. Если вкратце, то это блок кода, который можно приостановить, не блокируя поток. У корутины может быть контекст выполнения (CoroutineContext), цикл жизни корутины управляется Job. У корутины есть область действия (CoroutineScope), а поток, в котором она исполняется, задается с помощью CoroutineDispatcher.
Если проводить аналогию с iOS, то это похоже на выполнение блока кода в DispatchQueue, имеющей определенный QoS и привязку к определенному потоку NSThread, либо Operation в OperationQueue, где GlobalScope аналогичен DispatchQueue.global(), а MainScope DispatchQueue.main:
//Androidfun loadMovies() {  GlobalScope.async {    service.makeRequest()   withContext(uiDispatcher) {//...}  }}

//iOSfunc loadMovies() {  DispatchQueue.global().async {    service.makeRequest()  DispatchQueue.main.async{//...}}}

Еще одной ключевой особенностью корутин является использование слова suspend. Данный модификатор не превращает метод в асинхронный сам по себе, это зависит от других деталей реализации, но маркирует, что его можно приостановить без блокировки потока. Также такой метод можно вызывать только в контексте корутины.
Ktor использует механизм корутины для реализации асинхронной работы, поэтому вызов HttpClient делаем в suspended функции:
//Network serviceclass NetworkService {     val httpClient = HttpClient {        install(JsonFeature) {            val json = kotlinx.serialization.json.Json { ignoreUnknownKeys = true }            serializer = KotlinxSerializer(json)        }    }    suspend inline fun <reified T> loadData(url: String): T? {       return httpClient.get(url)    }}//Movies service suspend fun loadMovies():MoviesList? {        val url = MY_URL        return networkService.loadData<MoviesList>(url)    }

При подключении Kotlin Coroutines мы не указали никакую особую версию для iOS. Это не ошибка. Дело в том, что начиная с версии Kotlin 1.4 Suspended функция Kotlin легко трансформируется в функцию Swift c completion handler блоком:
func getMovies() {    self.networkService?.loadMovies {(movies, error) in       //...    }}

Т.к Ktor уже обеспечивает асинхронность, то в данном случае потребности в использовании DispatchQueue на стороне iOS нет.

На стороне Android используем механизм корутинов, и вызов будет иметь вид:
fun getMovies() {    mainScope.launch {    val movies = this.networkService?.loadMovies()//...    }}}


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

Если же мы хотим сделать максимально расшариваемый между наивными проектами общий код, включить туда архитектурное решение, а взаимодействие с UI через протоколы, то нам потребуется поменять работу и с потоками.

Посмотрим это в следующей части

Исходники примера github.com/anioutkazharkova/movies_kmp
Подробнее о работе корутин вы можете узнать тут

Подробнее..

Перевод Как выбрать мобильную кросс-платформу в 2021 году

25.02.2021 20:10:38 | Автор: admin

Кросс-платформенные решения - тренд в мобильной разработке. Уже есть различные технологии от PWA до Flutter и Kotlin Multiplatform. Как выбрать среди них?

Новый развивающийся бизнес зачастую в первую очередь ориентируется на мобильные технологии: социальные сети, необанкинговые решения, приложения для электронной коммерции, такси и другие. Новый бизнес ориентирован на экономическую эффективность, поэтому переход на кросс-платформенность для разработки мобильного приложения кажется правильным выбором. Посмотрим, что будет в 2021 году и как выбрать правильную технологию.

Познакомимся с Женей

Евгения солюшн-архитектор. Она должна решить, как построить новое мобильное приложение для изучения английского языка не носителями: людьми из Турции, Италии или России. Давайте посмотрим, как Женя подходит к этой задаче.

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

В компании владельце приложения также ожидают, что разработкой будет заниматься единая команда как для Android- и iOS-приложений, чтобы свести к минимуму усилия по передаче знаний и максимизировать скорость команды. В будущем также планируют запустить веб-приложения. А еще в компании хотели бы упростить будущий найм.

Прогрессивные веб-приложения

Женя начинает свои исследования. Она гуглит мобильные веб-приложения и находит статью. В ней упоминаются Прогрессивные веб-приложения (PWA). Что это такое?

Прогрессивные веб-приложения это, по сути, веб-сайты, которые используют специальные API для доступа к определенным возможностям устройства. Эти API позволяют получить доступ к памяти на устройстве, интегрируются с Push Notifications (на Android) и, что самое важное, работать в отдельной вкладке браузера. Еще их можно установить на устройство иконкой, как настоящее приложение. Звучит неплохо! Давайте посмотрим на плюсы и минусы PWA:

Плюсы:

  • Всем занимается одна команда.

  • Достаточно навыков веб-разработки.

  • Легко обслуживать.

  • Все работает сразу из коробки.

Минусы:

  • Нет пушей на iOS.

  • Неудобный UX.

  • Ограниченная поддержка.

  • Трудно найти пользователи скачивают приложение в сторах.

А вот и хорошая табличка, показывающая доступность PWA в разных браузерах:

Гибридные приложения

Жене не нравится, что PWA обладают ограниченной поддержкой пушей. Поэтому она гуглит дальше.

Она обнаруживает гибридный подход: давайте поместим наш сайт в WebView в реальном нативном приложении. Оказывается, можно поместить свой мобильный сайт со всем его HTML, CSS и JS-кодом в ресурсы приложения. Затем вы помещаете WebView в корневой ViewController/Activity приложения и указываете на эти ресурсы.

Пользователи смогут найти приложение в Google Play и AppStore, чего не было в случае с PWA. А еще, если вам нужны дополнительные возможности, вы можете легко добавить их ведь у вас настоящее приложение, для этого уже есть необходимые фреймворки.

Плюсы:

  • Высокая скорость работы.

  • Настоящая кросс-платформенность.

  • Можно пользоваться практически всеми преимуществами нативных приложений.

Минусы:

  • UX не очень (ведь перед нами по-прежнему сайт в шкуре приложения).

  • Небольшое сообщество разработчиков.

Архитектура гибридного приложенияАрхитектура гибридного приложения

Примеры таких приложений: Appcelerator, Ionic, Apache Cordova.

Нативные кросс-платформенные приложения

Большой недостаток гибридных приложений плохой UX. Поскольку это все еще браузер в мобильном приложении, он подтормаживает.

Евгения все еще пытается получить единую кодовую базу для своего решения, но при этом решить проблему с вялым UX. Поэтому она читает больше про нативные кросс-платформенные фреймворки.

Xamarin

Xamarin это решение на основе .NET от Microsoft.

Xamarin (или NET 5.0) помогает кросс-платформенное приложение на C# и .NET в мобильном приложении. Xamarin Forms (или MAUI) это библиотека для построения пользовательского интерфейса в таких приложениях. Теперь они оба переименованы:

Плюсы:

  • Легко разрабатывать в компаниях с сильной экспертизой в технологиях Microsoft.

  • Полностью кросс-платформенное.

  • Потенциально можно сделать его и веб-приложением.

Минусы:

  • 2 виртуальные машины в приложении на Android - JVM и Mono

  • Мало инструментов и библиотек

  • Долгое время исправления багов в Xamarin

  • Растет размер бандлов

React Native

React Native создан для того, чтобы привнести React в мобильные технологии. И это действительно хорошо сработало! Согласно статистике Bitrise, React Native занимает 13% рынка. Его также используют более 500 тысяч репозиториев на Гитхабе.

React Native использует языки Javascript или TypeScript для разработки приложений и транслирует компоненты React Native в компоненты платформы. Так, превращается в TextView на Android.

Приложение включает в себя JavaScript VM для выполнения JS-кода логики и пользовательского интерфейса приложения. React Native также умеет работать с API платформы. С такой архитектурой можно создать любое приложение, даже мобильную игру.

Плюсы:

  • Большое сообщество.

  • Пользуются в Skype, Discord, Shopify, Tesla, Artsy.

  • Доступны нативные возможности приложений.

  • Можно писать на React.

Минусы:

  • Долгое время запуска.

  • Экстеншены и работа в фоне требует написания нативного кода.

  • Проходит ре-архитектуризацию.

Flutter

Flutter это кросс-платформенный фреймворк с открытым исходным кодом от Google, основанный на языке Dart. В отличие от React Native, Flutter использует свой собственный 2D-движок для отрисовки пользовательского интерфейса в нативном виде. Такой подход обеспечивает независимость от версии ОС, на которой работает приложение. Flutter приложение также компилирует Dart в нативный для платформы код в релизной сборке, таким образом в рантайме Dart VM становится не нужна. Советую нашей Жене мое подробное сравнение Flutter и React Native.

Flutter привлек к себе большое внимание, собрал более 100 000 звезд, а количество приложений, собранных с его помощью, продолжает стремительно расти. Согласно той же статистике Bitrise, уже 9% билдов в прошлом году собраны на Flutter. Это очень много для такой молодой технологии.

Плюсы:

  • Хорошая производительность

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

  • Поддерживает веб

Минусы:

  • Ненативный UI

  • Малопопулярный язык Dart.

  • Недостаток некоторых инструментов (например, в плане контроля безопасности).

Kotlin Multiplatform

Xamarin, Flutter и React Native позволяют вам написать практически весь код единожды и запускать его и на iOS, и на Android.

Kotlin Multiplatform делает иначе. KMP считает, что пользовательский интерфейс сильно зависит от платформы и самого устройства (скажем, планшета или веб-сайта). Однако бизнес-логика остается практически неизменной. Почему бы не переиспользовать прежде всего ее?

Итак, с KMP у вас все еще есть два нативных приложения, которые пользуются одной бизнес-логикой. Вы можете использовать все, что угодно в пользовательском интерфейсе: будь то родной Android Views, JetPack Compose или Swift UI для iOS. Вы даже можете использовать Flutter или React Native для вашего пользовательского интерфейса! Он все равно будет прекрасно работать с Kotlin Multiplatform. Вот несколько примеров.

Плюсы:

  • Родной язык для разработчиков Android.

  • iOS разработчикам нетрудно читать Kotlin код.

  • Единая среда разработки для Android и iOS.

  • Подходит для реализации веб-приложений.

Минусы:

  • Молодое решение, все еще в альфа-версии.

  • Нельзя переиспользовать UI.

Что же выбрать?

Итак, наша Женя собирается выбрать свое решение. Вот что мы ей посоветуем:

  • Согласна на мобильный сайт вместо приложения, несмотря на ограничения в UI? Тогда PWA.

  • В компании все пишут на .NET? Значит, Xamarin.

  • Основной стек это JS/TypeScript? Выбираем React Native.

  • Будем шарить между приложениями не UI, а только бизнес-логику? Значит, Kotlin Multiplatform.

Конечно, это не все критерии: например, мы не рассмотрели доступность разработчиков на рынке или темы поддержки новых версий OS. Но основной алгоритм - выше :)

Удачи, Женя!

Подробнее..

Категории

Последние комментарии

  • Имя: Макс
    24.08.2022 | 11:28
    Я разраб в IT компании, работаю на арбитражную команду. Мы работаем с приламы и сайтами, при работе замечаются постоянные баны и лаги. Пацаны посоветовали сервис по анализу исходного кода,https://app Подробнее..
  • Имя: 9055410337
    20.08.2022 | 17:41
    поможем пишите в телеграм Подробнее..
  • Имя: sabbat
    17.08.2022 | 20:42
    Охренеть.. это просто шикарная статья, феноменально круто. Большое спасибо за разбор! Надеюсь как-нибудь с тобой связаться для обсуждений чего-либо) Подробнее..
  • Имя: Мария
    09.08.2022 | 14:44
    Добрый день. Если обладаете такой информацией, то подскажите, пожалуйста, где можно найти много-много материала по Yggdrasil и его уязвимостях для написания диплома? Благодарю. Подробнее..
© 2006-2024, personeltest.ru