From af0befe6d0c0fdcdee60a65113f83c86b0c87473 Mon Sep 17 00:00:00 2001
From: Kuzmin <«vkuzmin@icerockdev.com»>
Date: Fri, 15 May 2026 16:32:10 +0300
Subject: [PATCH 1/5] edit moko-resources
---
learning/libraries/moko/moko-resources.md | 153 +++++++++++++++++++++-
1 file changed, 150 insertions(+), 3 deletions(-)
diff --git a/learning/libraries/moko/moko-resources.md b/learning/libraries/moko/moko-resources.md
index e246258bf..adf2f225d 100644
--- a/learning/libraries/moko/moko-resources.md
+++ b/learning/libraries/moko/moko-resources.md
@@ -4,9 +4,156 @@ sidebar_position: 2
# moko-resources
-## Почему StringDesc не Parcelable
+Библиотека [moko-resources](https://github.com/icerockdev/moko-resources) — это решение для доступа к ресурсам (строки, изображения, цвета, шрифты, файлы) из общего Kotlin Multiplatform кода. Поддерживает Android, iOS, macOS, JVM, JS/Wasm и Compose Multiplatform.
-StringDesc не может быть Parcelable так как у нас есть ResourceFormattedStringDesc
+## Возможности
+
+- **Строки и плюралы** с поддержкой локализации — общий код для всех платформ
+- **StringDesc** — lifecycle-safe контейнер для строк, позволяющий отложить получение строки до момента, когда доступен platform context
+- **Цвета** с поддержкой light/dark темы
+- **Изображения** (SVG, PNG, JPG) с light/dark режимом
+- **Шрифты** (TTF, OTF)
+- **Файлы** (raw/assets) для Android
+- **Compose Multiplatform** — все ресурсы доступны как `painterResource`, `stringResource`, `colorResource` и т.д.
+
+## Подключение
+
+```kotlin
+// root build.gradle.kts
+buildscript {
+ dependencies {
+ classpath("dev.icerock.moko:resources-generator:0.26.4")
+ }
+}
+```
+
+```kotlin
+// shared/build.gradle.kts
+plugins {
+ kotlin("multiplatform")
+ id("dev.icerock.mobile.multiplatform-resources")
+}
+
+dependencies {
+ commonMainApi("dev.icerock.moko:resources:0.26.4")
+ // для Compose Multiplatform:
+ commonMainApi("dev.icerock.moko:resources-compose:0.26.4")
+ commonTestImplementation("dev.icerock.moko:resources-test:0.26.4")
+}
+
+multiplatformResources {
+ resourcesPackage.set("com.example.app") // обязательный package
+ resourcesClassName.set("SharedRes") // опционально, по умолчанию MR
+ resourcesVisibility.set(MRVisibility.Internal)
+}
+```
+
+## Использование
+
+Вот несколько примеров, подробнее смотрите в [moko-resources](https://github.com/icerockdev/moko-resources)
+
+### Строки (strings.xml)
+
+```xml
+
+
+ Hello
+ Test data %d
+ second string %2$s first decimal %1$d
+
+```
+
+```kotlin
+// commonMain — получение StringDesc
+val hello: StringDesc = MR.strings.hello.desc()
+val formatted: StringDesc = MR.strings.format.format(9)
+val positional: StringDesc = MR.strings.positional.format(9, "str")
+```
+
+```kotlin
+// Android — преобразование в String
+val text: String = hello.toString(context = this)
+
+// iOS
+let text: String = hello.localized()
+```
+
+### Изображения
+
+PNG/JPG имена файлов должны содержать суффикс плотности (`@1x`, `@2x`, `@3x`, `@4x`).
+
+```kotlin
+val image: ImageResource = MR.images.home_black_18
+val vector: ImageResource = MR.images.car_black // SVG
+
+// Android
+imageView.setImageResource(image.drawableResId)
+
+// iOS
+imageView.image = image.toUIImage()
+
+// Compose
+Image(painter = painterResource(MR.images.car_black), contentDescription = null)
+```
+
+Поддержка Dark Mode: добавьте `-dark` к имени файла:
+
+- `car.svg`
+- `car-dark.svg`
+
+### Файлы и ассеты
+
+```kotlin
+// Чтение текстового файла
+val text: String = MR.files.test_txt.readText() // common
+val text: String = MR.files.test_txt.getText(context) // Android
+
+// Compose — реактивное чтение
+val content: String? by MR.files.test_txt.readTextAsState()
+```
+
+## Compose Multiplatform
+
+Если подключён модуль `resources-compose`, все ресурсы доступны напрямую в `commonMain`:
+
+```kotlin
+// Строки
+Text(text = stringResource(MR.strings.hello_world))
+
+// Плюралы
+Text(text = pluralStringResource(MR.plurals.chars_count, counter, counter))
+
+// Цвета
+Text(color = colorResource(MR.colors.textColor), text = "Hello")
+
+// Изображения
+Image(painter = painterResource(MR.images.moko_logo), contentDescription = null)
+
+// Шрифты
+Text(fontFamily = fontFamilyResource(MR.fonts.cormorant_italic), text = "Hello")
+
+// Файлы
+val fileContent: String? by MR.files.some_file_txt.readTextAsState()
+```
+
+## Multi-module проекты
+
+Если ресурсы лежат в отдельном модуле, плагин нужно применить и в модуле с ресурсами, и в модуле, который собирается в framework.
+
+Для вложенного модуля можно задать кастомное имя класса:
+
+```kotlin
+multiplatformResources {
+ resourcesPackage.set("com.example.nested")
+ resourcesClassName.set("NestedMR")
+}
+```
+
+---
+
+## Почему StringDesc не Parcelable
+
+StringDesc не может быть Parcelable так как у нас есть ResourceFormattedStringDesc:
```kotlin
actual data class ResourceFormattedStringDesc actual constructor(
@@ -23,4 +170,4 @@ actual data class ResourceFormattedStringDesc actual constructor(
}
```
-Так как его аргументы типа Any, значит класс ResourceFormattedStringDesc не может быть parcelable, а значит и StringDesc не может быть Parcelable
\ No newline at end of file
+Так как его аргументы типа `Any`, класс `ResourceFormattedStringDesc` не может быть Parcelable, а значит и `StringDesc` не может быть Parcelable.
From 1b9e8205a312af7bf7c39b98836c2af081bc04be Mon Sep 17 00:00:00 2001
From: Kuzmin <«vkuzmin@icerockdev.com»>
Date: Fri, 15 May 2026 16:53:25 +0300
Subject: [PATCH 2/5] edit moko-mvvm
---
learning/libraries/moko/moko-mvvm.md | 129 ++++++++++++++++++++++++++-
1 file changed, 127 insertions(+), 2 deletions(-)
diff --git a/learning/libraries/moko/moko-mvvm.md b/learning/libraries/moko/moko-mvvm.md
index a4c3320e0..8f5d9228a 100644
--- a/learning/libraries/moko/moko-mvvm.md
+++ b/learning/libraries/moko/moko-mvvm.md
@@ -4,9 +4,134 @@ sidebar_position: 3
# moko-mvvm
-Библиотека [moko-mvvm](https://github.com/icerockdev/moko-mvvm) позволяет реализовывать приложения с паттерном
-Model - View - ViewModel с ViewModel в общем коде.
+Библиотека [moko-mvvm](https://github.com/icerockdev/moko-mvvm) предоставляет архитектурные компоненты Model-View-ViewModel для Kotlin Multiplatform. ViewModel работает в общем коде, на Android обеспечивается lifecycle-aware поведение через интеграцию с `androidx.lifecycle`.
+
+## Возможности
+
+- **ViewModel** — хранение и управление UI-данными с `viewModelScope` (CoroutineScope, отменяемый в `onCleared`)
+- **LiveData / Flow** — реактивные обёртки с операторами (`map`, `merge`, `combine`, `all`)
+- **EventsDispatcher** — отправка одноразовых событий из ViewModel во View с контролем lifecycle
+- **CFlow / CStateFlow / CMutableStateFlow** — обёртки над корутинными Flow, совместимые со Swift
+- **ResourceState** — sealed class для состояний: `Loading`, `Success`, `Empty`, `Failed`
+- **Compose Multiplatform / SwiftUI** — готовая интеграция
+
+## Требования
+
+- Android API 16+
+- iOS 11.0+
+- Gradle 6.8+
+
+## Подключение
+
+```kotlin
+// shared/build.gradle.kts
+dependencies {
+ commonMainApi("dev.icerock.moko:mvvm-core:0.16.1")
+ commonMainApi("dev.icerock.moko:mvvm-flow:0.16.1")
+ commonMainApi("dev.icerock.moko:mvvm-state:0.16.1")
+
+ // Compose Multiplatform
+ commonMainApi("dev.icerock.moko:mvvm-compose:0.16.1")
+ commonMainApi("dev.icerock.moko:mvvm-flow-compose:0.16.1")
+
+ commonTestImplementation("dev.icerock.moko:mvvm-test:0.16.1")
+}
+```
+
+Экспорт в iOS framework:
+
+```kotlin
+kotlin {
+ targets.withType(KotlinNativeTarget::class.java).all {
+ binaries.withType(Framework::class.java).all {
+ export("dev.icerock.moko:mvvm-core:0.16.1")
+ export("dev.icerock.moko:mvvm-flow:0.16.1")
+ }
+ }
+}
+```
+
+## Simple ViewModel — счётчик на CStateFlow
+
+```kotlin
+// commonMain
+class SimpleViewModel : ViewModel() {
+ private val _counter = MutableStateFlow(0)
+ val counter: CStateFlow =
+ _counter.map { it.toString() }
+ .stateIn(viewModelScope, SharingStarted.Lazily, "0")
+ .cStateFlow()
+
+ fun onCounterButtonPressed() {
+ _counter.value++
+ }
+}
+```
+
+**Compose:**
+
+```kotlin
+@Composable
+fun CounterScreen(
+ viewModel: SimpleViewModel = getViewModel(
+ factory = createViewModelFactory { SimpleViewModel() }
+ )
+) {
+ val counter: String by viewModel.counter.collectAsState()
+
+ Column {
+ Text(text = counter)
+ Button(onClick = { viewModel.onCounterButtonPressed() }) {
+ Text("Press me")
+ }
+ }
+}
+```
+
+**SwiftUI:**
+
+```swift
+struct CounterView: View {
+ @ObservedObject var viewModel: SimpleViewModel = SimpleViewModel()
+
+ var body: some View {
+ VStack {
+ Text(viewModel.counter.state(\.counter))
+ Button("Press me") { viewModel.onCounterButtonPressed() }
+ }
+ }
+}
+```
+
+## ResourceState
+
+```kotlin
+sealed class ResourceState {
+ class Loading : ResourceState()
+ data class Success(val data: T) : ResourceState()
+ class Empty : ResourceState()
+ data class Failed(val error: E) : ResourceState()
+}
+```
+
+```kotlin
+val state: CStateFlow, StringDesc>> = ...
+
+// Compose
+when (val s = state.collectAsState().value) {
+ is ResourceState.Loading -> LoadingState()
+ is ResourceState.Success -> BookList(s.data)
+ is ResourceState.Empty -> EmptyState()
+ is ResourceState.Failed -> ErrorState(s.error)
+}
+```
+
+## Дополнительный материал
+
+- [Референс библиотеки](https://github.com/icerockdev/moko-mvvm)
+- [Семплы (DataBinding)](https://github.com/icerockdev/moko-mvvm/tree/master/sample)
+- [Семплы (Compose + SwiftUI)](https://github.com/icerockdev/moko-mvvm/tree/master/sample-declarative-ui)
From 86ff193e6d95c0daecf55763e769638fd13e50d0 Mon Sep 17 00:00:00 2001
From: Kuzmin <«vkuzmin@icerockdev.com»>
Date: Fri, 15 May 2026 16:56:39 +0300
Subject: [PATCH 3/5] move moko-units
---
learning/{libraries/moko => legacy}/moko-units.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
rename learning/{libraries/moko => legacy}/moko-units.md (99%)
diff --git a/learning/libraries/moko/moko-units.md b/learning/legacy/moko-units.md
similarity index 99%
rename from learning/libraries/moko/moko-units.md
rename to learning/legacy/moko-units.md
index 264155958..f18e88590 100644
--- a/learning/libraries/moko/moko-units.md
+++ b/learning/legacy/moko-units.md
@@ -1,5 +1,5 @@
---
-sidebar_position: 4
+sidebar_position: 6
---
# moko-units
From 017698b4d85d62bf9f839f0dbd333f3adfacd840 Mon Sep 17 00:00:00 2001
From: Kuzmin <«vkuzmin@icerockdev.com»>
Date: Mon, 18 May 2026 10:24:37 +0300
Subject: [PATCH 4/5] fix detekt
---
learning/libraries/moko/moko-mvvm.md | 2 +-
university/6-lists/moko-units.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/learning/libraries/moko/moko-mvvm.md b/learning/libraries/moko/moko-mvvm.md
index 8f5d9228a..43b68522e 100644
--- a/learning/libraries/moko/moko-mvvm.md
+++ b/learning/libraries/moko/moko-mvvm.md
@@ -10,7 +10,7 @@ sidebar_position: 3
- **ViewModel** — хранение и управление UI-данными с `viewModelScope` (CoroutineScope, отменяемый в `onCleared`)
- **LiveData / Flow** — реактивные обёртки с операторами (`map`, `merge`, `combine`, `all`)
-- **EventsDispatcher** — отправка одноразовых событий из ViewModel во View с контролем lifecycle
+- **EventsDispatcher** — отправка одноразовых событий из ViewModel во View с контролем lifecycle (устаревший подход)
- **CFlow / CStateFlow / CMutableStateFlow** — обёртки над корутинными Flow, совместимые со Swift
- **ResourceState** — sealed class для состояний: `Loading`, `Success`, `Empty`, `Failed`
- **Compose Multiplatform / SwiftUI** — готовая интеграция
diff --git a/university/6-lists/moko-units.md b/university/6-lists/moko-units.md
index eacf3b6e1..57179e431 100644
--- a/university/6-lists/moko-units.md
+++ b/university/6-lists/moko-units.md
@@ -4,4 +4,4 @@ sidebar_position: 2
# moko-units
-Ознакомьтесь со всеми материалами со страницы [moko-units в базе знаний](/learning/libraries/moko/moko-units/).
+Ознакомьтесь со всеми материалами со страницы [moko-units в базе знаний](/learning/legacy/moko-units/).
From a100546220e5687d42d6915e84cfd234aff025fe Mon Sep 17 00:00:00 2001
From: Kuzmin <«vkuzmin@icerockdev.com»>
Date: Thu, 21 May 2026 15:19:09 +0300
Subject: [PATCH 5/5] fix after review
---
learning/libraries/moko/moko-mvvm.md | 2 +-
learning/libraries/moko/moko-resources.md | 2 +-
2 files changed, 2 insertions(+), 2 deletions(-)
diff --git a/learning/libraries/moko/moko-mvvm.md b/learning/libraries/moko/moko-mvvm.md
index 43b68522e..de872548c 100644
--- a/learning/libraries/moko/moko-mvvm.md
+++ b/learning/libraries/moko/moko-mvvm.md
@@ -8,7 +8,7 @@ sidebar_position: 3
## Возможности
-- **ViewModel** — хранение и управление UI-данными с `viewModelScope` (CoroutineScope, отменяемый в `onCleared`)
+- **ViewModel** — хранение и управление UI и данными с `viewModelScope` (CoroutineScope, отменяемый в `onCleared`)
- **LiveData / Flow** — реактивные обёртки с операторами (`map`, `merge`, `combine`, `all`)
- **EventsDispatcher** — отправка одноразовых событий из ViewModel во View с контролем lifecycle (устаревший подход)
- **CFlow / CStateFlow / CMutableStateFlow** — обёртки над корутинными Flow, совместимые со Swift
diff --git a/learning/libraries/moko/moko-resources.md b/learning/libraries/moko/moko-resources.md
index adf2f225d..02c2bbc4b 100644
--- a/learning/libraries/moko/moko-resources.md
+++ b/learning/libraries/moko/moko-resources.md
@@ -153,7 +153,7 @@ multiplatformResources {
## Почему StringDesc не Parcelable
-StringDesc не может быть Parcelable так как у нас есть ResourceFormattedStringDesc:
+StringDesc не может быть Parcelable, так как у нас есть ResourceFormattedStringDesc:
```kotlin
actual data class ResourceFormattedStringDesc actual constructor(