Skip to content

Commit b153c6b

Browse files
authored
Merge pull request #174 from KuzminVik/moko-resources
Moko resources
2 parents 555ac78 + a100546 commit b153c6b

4 files changed

Lines changed: 279 additions & 7 deletions

File tree

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
---
2-
sidebar_position: 4
2+
sidebar_position: 6
33
---
44

55
# moko-units

learning/libraries/moko/moko-mvvm.md

Lines changed: 127 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,134 @@ sidebar_position: 3
44

55
# moko-mvvm
66

7-
Библиотека [moko-mvvm](https://github.com/icerockdev/moko-mvvm) позволяет реализовывать приложения с паттерном
8-
Model - View - ViewModel с ViewModel в общем коде.
7+
Библиотека [moko-mvvm](https://github.com/icerockdev/moko-mvvm) предоставляет архитектурные компоненты Model-View-ViewModel для Kotlin Multiplatform. ViewModel работает в общем коде, на Android обеспечивается lifecycle-aware поведение через интеграцию с `androidx.lifecycle`.
8+
9+
## Возможности
10+
11+
- **ViewModel** — хранение и управление UI и данными с `viewModelScope` (CoroutineScope, отменяемый в `onCleared`)
12+
- **LiveData / Flow** — реактивные обёртки с операторами (`map`, `merge`, `combine`, `all`)
13+
- **EventsDispatcher** — отправка одноразовых событий из ViewModel во View с контролем lifecycle (устаревший подход)
14+
- **CFlow / CStateFlow / CMutableStateFlow** — обёртки над корутинными Flow, совместимые со Swift
15+
- **ResourceState** — sealed class для состояний: `Loading`, `Success`, `Empty`, `Failed`
16+
- **Compose Multiplatform / SwiftUI** — готовая интеграция
17+
18+
## Требования
19+
20+
- Android API 16+
21+
- iOS 11.0+
22+
- Gradle 6.8+
23+
24+
## Подключение
25+
26+
```kotlin
27+
// shared/build.gradle.kts
28+
dependencies {
29+
commonMainApi("dev.icerock.moko:mvvm-core:0.16.1")
30+
commonMainApi("dev.icerock.moko:mvvm-flow:0.16.1")
31+
commonMainApi("dev.icerock.moko:mvvm-state:0.16.1")
32+
33+
// Compose Multiplatform
34+
commonMainApi("dev.icerock.moko:mvvm-compose:0.16.1")
35+
commonMainApi("dev.icerock.moko:mvvm-flow-compose:0.16.1")
36+
37+
commonTestImplementation("dev.icerock.moko:mvvm-test:0.16.1")
38+
}
39+
```
40+
41+
Экспорт в iOS framework:
42+
43+
```kotlin
44+
kotlin {
45+
targets.withType(KotlinNativeTarget::class.java).all {
46+
binaries.withType(Framework::class.java).all {
47+
export("dev.icerock.moko:mvvm-core:0.16.1")
48+
export("dev.icerock.moko:mvvm-flow:0.16.1")
49+
}
50+
}
51+
}
52+
```
53+
54+
## Simple ViewModel — счётчик на CStateFlow
55+
56+
```kotlin
57+
// commonMain
58+
class SimpleViewModel : ViewModel() {
59+
private val _counter = MutableStateFlow(0)
60+
val counter: CStateFlow<String> =
61+
_counter.map { it.toString() }
62+
.stateIn(viewModelScope, SharingStarted.Lazily, "0")
63+
.cStateFlow()
64+
65+
fun onCounterButtonPressed() {
66+
_counter.value++
67+
}
68+
}
69+
```
70+
71+
**Compose:**
72+
73+
```kotlin
74+
@Composable
75+
fun CounterScreen(
76+
viewModel: SimpleViewModel = getViewModel(
77+
factory = createViewModelFactory { SimpleViewModel() }
78+
)
79+
) {
80+
val counter: String by viewModel.counter.collectAsState()
81+
82+
Column {
83+
Text(text = counter)
84+
Button(onClick = { viewModel.onCounterButtonPressed() }) {
85+
Text("Press me")
86+
}
87+
}
88+
}
89+
```
90+
91+
**SwiftUI:**
92+
93+
```swift
94+
struct CounterView: View {
95+
@ObservedObject var viewModel: SimpleViewModel = SimpleViewModel()
96+
97+
var body: some View {
98+
VStack {
99+
Text(viewModel.counter.state(\.counter))
100+
Button("Press me") { viewModel.onCounterButtonPressed() }
101+
}
102+
}
103+
}
104+
```
105+
106+
## ResourceState
107+
108+
```kotlin
109+
sealed class ResourceState<out T, out E> {
110+
class Loading<out T, out E> : ResourceState<T, E>()
111+
data class Success<out T, out E>(val data: T) : ResourceState<T, E>()
112+
class Empty<out T, out E> : ResourceState<T, E>()
113+
data class Failed<out T, out E>(val error: E) : ResourceState<T, E>()
114+
}
115+
```
116+
117+
```kotlin
118+
val state: CStateFlow<ResourceState<List<Book>, StringDesc>> = ...
119+
120+
// Compose
121+
when (val s = state.collectAsState().value) {
122+
is ResourceState.Loading -> LoadingState()
123+
is ResourceState.Success -> BookList(s.data)
124+
is ResourceState.Empty -> EmptyState()
125+
is ResourceState.Failed -> ErrorState(s.error)
126+
}
127+
```
128+
129+
## Дополнительный материал
9130

10131
<iframe src="//www.youtube.com/embed/qe8FcIQEmyA?list=PL6yFiPOVXVUi90sQ66dtmuXP-1-TeHwl5" frameborder="0" allowfullscreen width="675" height="380"></iframe>
11132
<br/>
12133
<br/>
134+
135+
- [Референс библиотеки](https://github.com/icerockdev/moko-mvvm)
136+
- [Семплы (DataBinding)](https://github.com/icerockdev/moko-mvvm/tree/master/sample)
137+
- [Семплы (Compose + SwiftUI)](https://github.com/icerockdev/moko-mvvm/tree/master/sample-declarative-ui)

learning/libraries/moko/moko-resources.md

Lines changed: 150 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,156 @@ sidebar_position: 2
44

55
# moko-resources
66

7-
## Почему StringDesc не Parcelable
7+
Библиотека [moko-resources](https://github.com/icerockdev/moko-resources) — это решение для доступа к ресурсам (строки, изображения, цвета, шрифты, файлы) из общего Kotlin Multiplatform кода. Поддерживает Android, iOS, macOS, JVM, JS/Wasm и Compose Multiplatform.
88

9-
StringDesc не может быть Parcelable так как у нас есть ResourceFormattedStringDesc
9+
## Возможности
10+
11+
- **Строки и плюралы** с поддержкой локализации — общий код для всех платформ
12+
- **StringDesc** — lifecycle-safe контейнер для строк, позволяющий отложить получение строки до момента, когда доступен platform context
13+
- **Цвета** с поддержкой light/dark темы
14+
- **Изображения** (SVG, PNG, JPG) с light/dark режимом
15+
- **Шрифты** (TTF, OTF)
16+
- **Файлы** (raw/assets) для Android
17+
- **Compose Multiplatform** — все ресурсы доступны как `painterResource`, `stringResource`, `colorResource` и т.д.
18+
19+
## Подключение
20+
21+
```kotlin
22+
// root build.gradle.kts
23+
buildscript {
24+
dependencies {
25+
classpath("dev.icerock.moko:resources-generator:0.26.4")
26+
}
27+
}
28+
```
29+
30+
```kotlin
31+
// shared/build.gradle.kts
32+
plugins {
33+
kotlin("multiplatform")
34+
id("dev.icerock.mobile.multiplatform-resources")
35+
}
36+
37+
dependencies {
38+
commonMainApi("dev.icerock.moko:resources:0.26.4")
39+
// для Compose Multiplatform:
40+
commonMainApi("dev.icerock.moko:resources-compose:0.26.4")
41+
commonTestImplementation("dev.icerock.moko:resources-test:0.26.4")
42+
}
43+
44+
multiplatformResources {
45+
resourcesPackage.set("com.example.app") // обязательный package
46+
resourcesClassName.set("SharedRes") // опционально, по умолчанию MR
47+
resourcesVisibility.set(MRVisibility.Internal)
48+
}
49+
```
50+
51+
## Использование
52+
53+
Вот несколько примеров, подробнее смотрите в [moko-resources](https://github.com/icerockdev/moko-resources)
54+
55+
### Строки (strings.xml)
56+
57+
```xml
58+
<!-- base/strings.xml -->
59+
<resources>
60+
<string name="hello">Hello</string>
61+
<string name="format">Test data %d</string>
62+
<string name="positional">second string %2$s first decimal %1$d</string>
63+
</resources>
64+
```
65+
66+
```kotlin
67+
// commonMain — получение StringDesc
68+
val hello: StringDesc = MR.strings.hello.desc()
69+
val formatted: StringDesc = MR.strings.format.format(9)
70+
val positional: StringDesc = MR.strings.positional.format(9, "str")
71+
```
72+
73+
```kotlin
74+
// Android — преобразование в String
75+
val text: String = hello.toString(context = this)
76+
77+
// iOS
78+
let text: String = hello.localized()
79+
```
80+
81+
### Изображения
82+
83+
PNG/JPG имена файлов должны содержать суффикс плотности (`@1x`, `@2x`, `@3x`, `@4x`).
84+
85+
```kotlin
86+
val image: ImageResource = MR.images.home_black_18
87+
val vector: ImageResource = MR.images.car_black // SVG
88+
89+
// Android
90+
imageView.setImageResource(image.drawableResId)
91+
92+
// iOS
93+
imageView.image = image.toUIImage()
94+
95+
// Compose
96+
Image(painter = painterResource(MR.images.car_black), contentDescription = null)
97+
```
98+
99+
Поддержка Dark Mode: добавьте `-dark` к имени файла:
100+
101+
- `car.svg`
102+
- `car-dark.svg`
103+
104+
### Файлы и ассеты
105+
106+
```kotlin
107+
// Чтение текстового файла
108+
val text: String = MR.files.test_txt.readText() // common
109+
val text: String = MR.files.test_txt.getText(context) // Android
110+
111+
// Compose — реактивное чтение
112+
val content: String? by MR.files.test_txt.readTextAsState()
113+
```
114+
115+
## Compose Multiplatform
116+
117+
Если подключён модуль `resources-compose`, все ресурсы доступны напрямую в `commonMain`:
118+
119+
```kotlin
120+
// Строки
121+
Text(text = stringResource(MR.strings.hello_world))
122+
123+
// Плюралы
124+
Text(text = pluralStringResource(MR.plurals.chars_count, counter, counter))
125+
126+
// Цвета
127+
Text(color = colorResource(MR.colors.textColor), text = "Hello")
128+
129+
// Изображения
130+
Image(painter = painterResource(MR.images.moko_logo), contentDescription = null)
131+
132+
// Шрифты
133+
Text(fontFamily = fontFamilyResource(MR.fonts.cormorant_italic), text = "Hello")
134+
135+
// Файлы
136+
val fileContent: String? by MR.files.some_file_txt.readTextAsState()
137+
```
138+
139+
## Multi-module проекты
140+
141+
Если ресурсы лежат в отдельном модуле, плагин нужно применить и в модуле с ресурсами, и в модуле, который собирается в framework.
142+
143+
Для вложенного модуля можно задать кастомное имя класса:
144+
145+
```kotlin
146+
multiplatformResources {
147+
resourcesPackage.set("com.example.nested")
148+
resourcesClassName.set("NestedMR")
149+
}
150+
```
151+
152+
---
153+
154+
## Почему StringDesc не Parcelable
155+
156+
StringDesc не может быть Parcelable, так как у нас есть ResourceFormattedStringDesc:
10157

11158
```kotlin
12159
actual data class ResourceFormattedStringDesc actual constructor(
@@ -23,4 +170,4 @@ actual data class ResourceFormattedStringDesc actual constructor(
23170
}
24171
```
25172

26-
Так как его аргументы типа Any, значит класс ResourceFormattedStringDesc не может быть parcelable, а значит и StringDesc не может быть Parcelable
173+
Так как его аргументы типа `Any`, класс `ResourceFormattedStringDesc` не может быть Parcelable, а значит и `StringDesc` не может быть Parcelable.

university/6-lists/moko-units.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,4 @@ sidebar_position: 2
44

55
# moko-units
66

7-
Ознакомьтесь со всеми материалами со страницы [moko-units в базе знаний](/learning/libraries/moko/moko-units/).
7+
Ознакомьтесь со всеми материалами со страницы [moko-units в базе знаний](/learning/legacy/moko-units/).

0 commit comments

Comments
 (0)