Skip to content
Open
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
21 changes: 21 additions & 0 deletions .github/compilation-check-source.yml
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,27 @@ jobs:
- *publish_test_report
- *upload_reports

check-android-kmp-library:
runs-on: ${{ matrix.os }}
strategy:
matrix:
<<: *runner_matrix
needs: build-library

steps:
- *checkout
- *setup_jdk
- *setup_gradle
- *cache_konan
- *download_maven

- name: Sample - android-kmp-library
run: cd samples/android-kmp-library && ./local-check.sh
shell: bash

- *publish_test_report
- *upload_reports

check-kotlin-2-tests:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
47 changes: 47 additions & 0 deletions .github/workflows/compilation-check.yml
Original file line number Diff line number Diff line change
Expand Up @@ -556,6 +556,53 @@ jobs:
name: code-coverage-report-${{ github.job }}-${{ matrix.os }}
path: "**/build/reports/**/*"

check-android-kmp-library:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os:
- macOS-latest
- windows-latest
- ubuntu-latest
needs: build-library
steps:
- uses: actions/checkout@v4
- name: Set up JDK 17
uses: actions/setup-java@v4
with:
java-version: 17
distribution: zulu
- name: Setup Gradle
uses: gradle/gradle-build-action@v3
with:
cache-read-only: ${{ github.ref != 'refs/heads/master' && github.ref !=
'refs/heads/develop' }}
- name: Cache .konan
uses: actions/cache@v4
with:
path: ~/.konan
key: ${{ matrix.os }}-konan-${{ hashFiles('**/*.gradle*', 'gradle/**/*') }}
- name: Download maven artifacts
uses: actions/download-artifact@v4
with:
name: maven
path: ~/.m2/repository/dev/icerock
- name: Sample - android-kmp-library
run: cd samples/android-kmp-library && ./local-check.sh
shell: bash
- name: Publish Test Report
uses: mikepenz/action-junit-report@v4
if: ${{ always() }}
with:
report_paths: "**/build/test-results/**/TEST-*.xml"
github_token: ${{ secrets.GITHUB_TOKEN }}
- name: Archive reports
uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: code-coverage-report-${{ github.job }}-${{ matrix.os }}
path: "**/build/reports/**/*"

check-kotlin-2-tests:
runs-on: ${{ matrix.os }}
strategy:
Expand Down
65 changes: 65 additions & 0 deletions COMPATIBILITY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
# Compatibility policy

Consumer minimums and the `resources-generator` compile toolchain are versioned independently.
Updating a generator build dependency does not implicitly raise a consumer requirement.

## Version matrix

| Scope | Version | Contract |
|----------------------|---------|--------------------------------------------------------------------------------------------|
| Consumer Kotlin | 2.1.0 | Minimum for published artifacts; also used as the generator language and API level. |
| Generator Kotlin | 2.4.20 | Build toolchain only; does not change the consumer minimum. |
| Generator KotlinPoet | 2.3.0 | Internal implementation dependency; absent from generated and public APIs. |
| Consumer AGP | 8.3.0 | Oldest supported AGP runtime. |
| Generator AGP API | 8.13.2 | Last AGP 8 compile API, used via `compileOnly`; the runtime matrix covers AGP 8 and AGP 9. |

## Kotlin baseline

The next planned consumer Kotlin baseline is 2.2.0. It replaces 2.1.0 after the Kotlin 2.5 line is
validated as stable by the compatibility matrix.

The baseline may move earlier if a Kotlin 2.5 compatibility fix cannot be released while retaining
Kotlin 2.1 support. A required moko-resources release must not be blocked on a later Kotlin 2.5.x
patch solely to preserve the previous baseline.

## KotlinPoet

KotlinPoet 2.4.0 is intentionally deferred. Its explicit-backing-field, multi-field value-class,
and code-comment APIs are not used by the generator. KotlinPoet should be upgraded separately when
a required API or fix justifies the change.

See the [KotlinPoet 2.4.0 release notes](https://github.com/square/kotlinpoet/releases/tag/2.4.0).

## Android Gradle Plugin

The generator compiles against AGP 8.13.2 to remain on the AGP 8 DSL ABI while supporting both
AGP 8 and AGP 9 runtimes. Compiling against the AGP 9 API is a separate migration because it drops
AGP 8 binary compatibility.

AGP 8.13.2 and Gradle 8.14.2 are unrelated version lines. The latter is a Gradle distribution
version used by compatibility samples, not a newer AGP 8 release.

See the [AGP 8.13 release notes](https://developer.android.com/build/releases/agp-8-13-0-release-notes).

## Apple KLib resources: 0.28.0 breaking KLib format change

Starting with moko-resources 0.28.0, Apple KLibs with image or color resources
contain raw `Assets.xcassets` instead of a precompiled `Assets.car`. This makes
the KLib portable across Windows, Linux, and macOS, but moves asset catalog
compilation to the macOS project that links the final Apple framework or
executable.

This is a breaking change to the KLib resource format, not to the runtime API.
Gradle plugins are not inherited transitively from library dependencies, so the
project that links the final Apple binary must choose a compatible plugin
version itself.

| Produced Apple KLib | Final Apple consumer Gradle plugin | Result |
| --- | --- | --- |
| `< 0.28.0`, contains `Assets.car` | Any version | Supported |
| `>= 0.28.0`, no image/color resources | `< 0.28.0` | Supported; no asset catalog compilation is required |
| `>= 0.28.0`, contains raw `Assets.xcassets` | `< 0.28.0` | Not supported for images and colors; the raw catalog is copied but not compiled |
| `>= 0.28.0`, contains raw `Assets.xcassets` | `>= 0.28.0` | Supported; `actool` runs while linking the final Apple binary |

Newer plugin versions remain compatible with older KLibs that already contain
`Assets.car`.
35 changes: 28 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,22 @@ implement all your UI in Kotlin with Jetpack Compose and MOKO resources.
- iOS version 12.0+
- Compose Multiplatform 1.6.0+

The consumer compatibility policy and generator toolchain constraints are documented in
[COMPATIBILITY.md](COMPATIBILITY.md).

### Apple KLib cross-compilation

With Kotlin 2.2.20 or newer, an Apple-target KLib can be compiled on Windows,
Linux, and macOS when neither the library nor its dependencies use cinterop or
CocoaPods. Linking, testing, and packaging a final Apple binary still require
macOS and Xcode.

Starting with moko-resources 0.28.0, Apple KLibs with image or color resources
use a new raw asset catalog format. The macOS project that links the final Apple
binary must use moko-resources Gradle plugin 0.28.0 or newer. See
[COMPATIBILITY.md](COMPATIBILITY.md#apple-klib-resources-0280-breaking-klib-format-change)
for the producer/consumer compatibility matrix.

## Installation

### Gradle setup
Expand Down Expand Up @@ -182,7 +198,8 @@ You should enable moko-resources gradle plugin in `resources` module, that conta

#### Android Host Tests (Unit Tests)
If you use the new Android Multiplatform Library plugin (`com.android.kotlin.multiplatform.library`),
enabling Android resources for host tests (Unit tests) depends on your AGP version.
moko-resources generates Android resources and `R` accessors for its Android target.
For host tests (Unit tests), enabling Android resources depends on your AGP version.
This is required for moko-resources to access generated R classes during testing.

For AGP 8.8.0 and higher
Expand Down Expand Up @@ -285,6 +302,10 @@ In Xcode add `Build Phase` (at end of list) with script:

`YourFrameworkName` is name of your project framework. Please, see on a static framework warning for get correct task name.

moko-resources automatically adds the framework bundle glob to the generated
podspec. Do not add the same resource glob manually; an explicit
`extraSpecAttributes["resource"]` value remains unchanged.

#### Without org.jetbrains.kotlin.native.cocoapods

In Xcode add `Build Phase` (at end of list) with script:
Expand Down Expand Up @@ -618,11 +639,11 @@ string source:

```kotlin
fun getUserName(user: User?): StringDesc {
if (user != null) {
return StringDesc.Raw(user.name)
} else {
return StringDesc.Resource(MR.strings.name_placeholder)
}
return if (user != null) {
StringDesc.Raw(user.name)
} else {
StringDesc.Resource(MR.strings.name_placeholder)
}
}
```

Expand Down Expand Up @@ -967,7 +988,7 @@ val assetContent: String? by MR.assets.test.readTextAsState()

### iOS shows key instead of localized text

1. check that generated `Localizable.strings` file is valid - open it by Xcode (located in `shared/shared/build/bin/iosSimulatorArm64/debugFramework/shared.framework/<project-name>:shared.bundle/Contents/Resources/Base.lproj/Localizable.strings` and in other `.lproj` directories. If Xcode show error in file - you should fix content of strings.xml (for example you use some special character that broke file).
1. check that generated `Localizable.strings` file is valid - open it by Xcode (located in `shared/shared/build/bin/iosSimulatorArm64/debugFramework/shared.framework/<resourcesPackage>.main.bundle/Contents/Resources/Base.lproj/Localizable.strings` and in other `.lproj` directories. If Xcode show error in file - you should fix content of strings.xml (for example you use some special character that broke file).

2. check that your generated `.bundle` exist inside application at runtime. In Xcode inside group `Products` select your application and click `Show in Finder`. Then click `Show Package Contents`. Inside `.app` you should see `.bundle` in root directory if you use static framework. And in `Frameworks/shared.framework` if you use dynamic framework. If `bundle` missed - check installation guide. Specifically xcode build phase part if you use static framework. And check that you apply moko-resources plugin in `shared` gradle module.

Expand Down
20 changes: 10 additions & 10 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,17 +1,16 @@
[versions]
# --- Runtime versions ---
# Minimum compatibility baseline for library modules (resources, compose, etc.).
# Allows users to use moko-resources WITHOUT forcing them to update their projects.
# --- Consumer compatibility baselines ---
# Raising these versions changes the supported consumer matrix; see COMPATIBILITY.md.
kotlinVersion = "2.1.0"
androidGradleVersion = "8.3.0"

# --- Library dependencies ---
androidSdkCommonVersion = "31.13.2"

# --- Plugin versions (Tooling only) ---
# Used EXCLUSIVELY in the generator plugin module via 'compileOnly'.
# We use current APIs (available in 8.13.2+) to ensure compatibility with
# the widest range of AGP versions: from the minimum (8.3)
# to the current (9.0+ at this time).
pluginKotlinVersion = "2.3.20"
# --- Generator compile toolchain ---
# KGP and AGP are compileOnly host APIs and do not redefine the consumer baselines above.
pluginKotlinVersion = "2.4.20"
# Last AGP 8 API. AGP 9 compile APIs are not binary-compatible with AGP 8 runtimes.
pluginAndroidGradleVersion = "8.13.2"


Expand All @@ -31,7 +30,8 @@ composeJetbrainsVersion = "1.7.0"

# jvm
apacheCommonsTextVersion = "1.10.0"
kotlinPoetVersion = "1.13.1"
# 2.4.0 has no API or fix currently required by the generator; see COMPATIBILITY.md.
kotlinPoetVersion = "2.3.0"
detektVersion = "1.22.0"
icu4jVersion = "73.1"
commonsCodecVersion = "1.15"
Expand Down
3 changes: 2 additions & 1 deletion resources-generator/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,8 @@ kotlin {
tasks.withType<KotlinJvmCompile>().configureEach {
compilerOptions {
jvmTarget.set(JvmTarget.JVM_11)
languageVersion.set(KotlinVersion.fromVersion("2.0"))
languageVersion.set(KotlinVersion.fromVersion("2.1"))
apiVersion.set(KotlinVersion.fromVersion("2.1"))
}
}

Expand Down
Loading
Loading