Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
sidebar_position: 4
sidebar_position: 6
---

# moko-units
Expand Down
129 changes: 127 additions & 2 deletions learning/libraries/moko/moko-mvvm.md
Original file line number Diff line number Diff line change
Expand Up @@ -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<String> =
_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<out T, out E> {
class Loading<out T, out E> : ResourceState<T, E>()
data class Success<out T, out E>(val data: T) : ResourceState<T, E>()
class Empty<out T, out E> : ResourceState<T, E>()
data class Failed<out T, out E>(val error: E) : ResourceState<T, E>()
}
```

```kotlin
val state: CStateFlow<ResourceState<List<Book>, 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)
}
```

## Дополнительный материал

<iframe src="//www.youtube.com/embed/qe8FcIQEmyA?list=PL6yFiPOVXVUi90sQ66dtmuXP-1-TeHwl5" frameborder="0" allowfullscreen width="675" height="380"></iframe>
<br/>
<br/>

- [Референс библиотеки](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)
153 changes: 150 additions & 3 deletions learning/libraries/moko/moko-resources.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
<!-- base/strings.xml -->
<resources>
<string name="hello">Hello</string>
<string name="format">Test data %d</string>
<string name="positional">second string %2$s first decimal %1$d</string>
</resources>
```

```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(
Expand All @@ -23,4 +170,4 @@ actual data class ResourceFormattedStringDesc actual constructor(
}
```

Так как его аргументы типа Any, значит класс ResourceFormattedStringDesc не может быть parcelable, а значит и StringDesc не может быть Parcelable
Так как его аргументы типа `Any`, класс `ResourceFormattedStringDesc` не может быть Parcelable, а значит и `StringDesc` не может быть Parcelable.
2 changes: 1 addition & 1 deletion university/6-lists/moko-units.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ sidebar_position: 2

# moko-units

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