Kotlin Multiplatform Broadcast
9.47K subscribers
821 photos
48 videos
1.09K links
Новости и фичи Kotlin, а также Kotlin Multiplatform

YouTubе канал: https://youtube.com/androidBroadcast
Compose Multiplatform @compose_broadcast
iOS разработка @ios_broadcast
Новости Android @android_broadcast
Реклама и прочее @android_broadcast_bot
Download Telegram
Для меня ключевой фичёй языка Kotlin является - Nullable типы (Null-safety)

Почему? Это позволило сделать систему типов более строгой, избавиться от проверок на null или наоборот принуждает их выполнить! После опыта Java именно проблема с null была самой большой (известная как “Billion Dollar Mistake”)!

Выделить отдельную фичу невероятно трудно, но я это делал именно по ценности для конечного продукта и его стабильности. Все возможности Kotlin как набор звуков - соединив их вместе, получаешь прекрасную музыку. Только как и с любым музыкальным инструментом в раках дилетанта может получиться что-то страшное и пугающее 🤬
This media is not supported in your browser
VIEW IN TELEGRAM
KtorCLI - утилита командной строки для генерации новых проектов на Ktor

#kotlin #ktor
Please open Telegram to view this post
VIEW IN TELEGRAM
🏝 Вышел Kotlin 2.1.10

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

#kotlin
Please open Telegram to view this post
VIEW IN TELEGRAM
This media is not supported in your browser
VIEW IN TELEGRAM
🚀 Вышло обновление Web Wizard генератора Ktor проекта

Что нового:
👉 Обновленный дизайн
👉 Упростили настройку проекта
👉 Превью проекта, который будет сгенерирован

#kotlin #ktor
Please open Telegram to view this post
VIEW IN TELEGRAM
Что бы вы изменили в это коде и почему?

fun PersonDto?.toPerson(): Person? = 
if (this == null) {
null
} else {
Person(
name = name,
surname = surname,
age = age
)
}
Kotlin Multiplatform Broadcast
Что бы вы изменили в это коде и почему? fun PersonDto?.toPerson(): Person? = if (this == null) { null } else { Person( name = name, surname = surname, age = age ) }
Не делайте single-expression функции, если она занимает у вас больше 1 строки

Пример кода из поста выше читается очень сложно из-за многострочного if/else, который используется как выражения для функции чтобы не писать тело. Правило просто - в функции больше одной строки, то точно объявляете тело {}

Исключения бывают, о них ниже

Код и поста станет выглядеть так
fun PersonDto?.toPerson(): Person? {
if (this == null) {
return null
} else {
return Person(
name = name,
surname = surname,
age = age
)
}
}
Когда полезно использовать single-exression fun

Есть удобные кейсы, когда многострочные single-expression функции могут быть полезны. Все они связаны с вызовом функции/инструкции с телом, например

fun a() = when(...)  {
// Позволяет сделать exhausted when,
// через обязательный возврат значений в каждой ветке

// Предпочитаю с каждом case делать только одну строчку,
// все что больше - переношу в функции и вызываю её

// С if-else так красиво не выглядит, но тоже можно
}

fun b() = buildList {
// билдер функция коллекции
}

fun c(...) = with(...) {
// Изменил this в блоке кода
}
‼️ Избегайте расширений для Nullable типов

Смысл делать расширения для Nullable типа бывает очень редко. В примере, что я привёл вам выше произошло только усложнения кода и добавления дополнительной инструкций т.к. null вернет null. Операторы для работы с Null safety (?. и ?:) прекрасно справляются с этим

Как понять стоит ли делать расширение для Nullable функции - объясните себе нестандартное поведение для null значения либо функция имеет суть только в случае Nullable типа

Обновленный код будет выглядеть так:
fun PersonDto.toPerson(): Person {
return Person(
name = name,
surname = surname,
age = age
)
}

val personDto: Person? = ...
val person: Person? = personDto?.toPerson()
Please open Telegram to view this post
VIEW IN TELEGRAM
Используйте trailing comma при работе с аргументами

trailing comma полезная фича, чтобы сделать историю в VCS боле красивой. Давайте на примере:
Person(
name = name,
surname = surname,
age = age // Без необязательной запятой после последнего аргумента
)

теперь мне надо добавить новое свойство и задавать его
1 Person(
2 name = name,
3 surname = surname,
4 age = age, // Добавил запятую для отделения инструкций
5 id = id
6)

После таких изменений в VCS отразиться изменения в 2 строчках - номер 4 и 5. Если же изначально я использовал trailing comma везде, то изменение было бы только в одной строке - 5.

Другой пользы от trailing comma фичи я не знаю, а вы можете поделиться в комментариях

UPD: в IDE ещё удобно менять порядок передачи именованных аргументов в многострочном вызове функции
Пример (EN,8М) автоматизации генерации классов UI модели на основе параметров Composable функциb. Используется KSP для генерации кода

🔗 Ссылка без VPN

#kotlin #compose #ksp
Не стоит использовать CoroutineScope, отвязанный от приложения

Порой в коде встречается ситуация, когда для запуска корутины в репозитории или каком-либо менеджере разработчики создают CoroutineScope:
class MyRepository(...) {

private val scope = CoroutineScope()

fun doWork(...) {
scope.launch { /* длительная работа */ }
}
}


Но есть вариант хуже:
class MyRepository(...) {

fun doWork(...) {
CoroutineScope(...).launch { /* длительная работа */ }
}
}


0️⃣ CoroutineScope не должен создаваться для запуска только одной корутины. ‼️

1️⃣ При создании CoroutineScope следует добавить Job (или SupervisorJob), а также CoroutineDispatcher, который будет использоваться по умолчанию.

2️⃣ CoroutineScope должен быть связан с каким-либо жизненным циклом объекта. Вся суть scope заключается в том, чтобы отменять операции, когда они больше не нужны. Например, viewModelScope привязан к жизни ViewModel. В Android приложениях я всегда создаю Scope, связанный с Android Application классом, - AppScope. Либо использовать AppScope в классах, где нужен scope, либо сделать scope, который будет связан с другим CoroutineScope приложения.

Создать дочерний Scope можно следующим образом:
fun CoroutineScope.childScope(
context: CoroutineContext = EmptyCoroutineContext
): CoroutineScope {
return CoroutineScope(
coroutineContext
+ SupervisorJob(parent = coroutineContext[Job])
+ context
)
}


3️⃣ Вам точно нужен запуск корутины? Или подойдет suspend функция? Определитесь, почему асинхронный код должен работать независимо от вызывающего его кода и не иметь возможности получения информации об окончании работы через suspend. Также можно возвращать объект Job (хотя это спорная практика, на мой взгляд).

4️⃣ Не используйте GlobalScope. ‼️ Это наследие старых корутин, когда ещё не было structured concurrency.

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

#kotlin #coroutines
Please open Telegram to view this post
VIEW IN TELEGRAM
Вышел Coil 3.1.0 - популярный загрузчик картинок с поддержкой KMP

👉 Улучшили производительность AsyncImage: скорость работы стала лучше на 25-40%, а потребление памяти - на 35-48%
👉 FakeImage теперь deprecated
👉 Появился ColorImage - полезен для возвращения фейковых значений в тестах и для Compose превью
👉 coil-compose-core больше не зависит от Dispatchers.Main.immedate, что позволило исправить баги в работе Paparazzi и Roborazzi
🛠 Множество других исправлений и улучшений API

#kmp #android #compose
Статья (EN,7м) с разбором как устроен под капотом и работает runBlocking { }.

#kotlin #coroutines
Используете runBlocking в продакшен коде приложений (не тесты)?
Anonymous Poll
32%
Да
57%
Нет
3%
Не знаю
7%
Не участвую в опросе