Skip to content

Repository files navigation

Kayan icon

Kayan

Typed layered JSON and YAML config for Kotlin Multiplatform, JVM, and Android.

CI Codecov coverage Maven Central Release License Docs

Kayan is a Kotlin Gradle plugin that generates a typed Kotlin object from layered JSON and YAML config files. It works with Kotlin Multiplatform, Kotlin/JVM, and Kotlin Android projects, so shared code can read config directly without platform-specific BuildConfig wiring.

The name comes from the Arabic word كيان (Kayan), which means "entity", "structure", or "being". JSON is valid YAML, but Kayan still makes the format choice explicit so your builds do not develop a split personality.

Why Kayan

  • Keep config in JSON or YAML while exposing a typed Kotlin API.
  • Let the consuming app own the schema and generated property names.
  • Resolve layered defaults and overrides deterministically at build time.
  • Work in shared Kotlin across KMP, JVM, and Android modules.
  • Reuse resolved values inside Gradle itself with buildValue("key").

Quick start

Add Maven Central to Gradle plugin resolution when consuming the published plugin:

pluginManagement {
    repositories {
        gradlePluginPortal()
        mavenCentral()
    }
}

Add a config file:

{
  "flavors": {
    "prod": {
      "api_base_url": "https://api.example.com",
      "feature_search_enabled": false
    },
    "dev": {
      "api_base_url": "https://dev.example.com",
      "feature_search_enabled": true
    }
  },
  "brand_name": "Example App"
}

YAML works too:

flavors:
  prod:
    api_base_url: https://api.example.com
    feature_search_enabled: false
  dev:
    api_base_url: https://dev.example.com
    feature_search_enabled: true
brand_name: Example App

Apply the plugin and declare your schema:

plugins {
    kotlin("multiplatform") version "<kotlin-version>"
    id("io.github.mohamadjaara.kayan") version "<kayan-version>"
}

kayan {
    packageName.set("sample.generated")
    flavor.set("prod")

    schema {
        string("api_base_url", "API_BASE_URL", required = true)
        boolean("feature_search_enabled", "FEATURE_SEARCH_ENABLED")
        string("brand_name", "BRAND_NAME")
    }
}

Generate the config source:

./gradlew generateKayanConfig

Use the generated object from shared code:

import sample.generated.SampleConfig

val baseUrl = SampleConfig.API_BASE_URL
val searchEnabled = SampleConfig.FEATURE_SEARCH_ENABLED
val brandName = SampleConfig.BRAND_NAME

Generated source lands in build/generated/kayan/kotlin and is wired into the appropriate source set automatically.

Target-specific overrides

For Kotlin Multiplatform projects that need one config API in shared code but different values per target, add targets in the config file and configure Kayan targets:

brand_name: Example App

flavors:
  prod:
    targets:
      android:
        bundle_id: com.example.android
      ios:
        bundle_id: com.example.ios
kayan {
    packageName.set("sample.generated")
    flavor.set("prod")

    targets("android", "ios")
}

Kayan generates an expect object into commonMain and matching actual object declarations into the configured target source sets.

For non-standard source set names or custom target labels, use the DSL form:

kayan {
    targets {
        ios()
        jvm("desktop")
        sourceSet("appleMain", "ios-shared")
    }
}

By design, Kayan is a fit for non-sensitive build and app configuration. Values declared in the schema can be generated into source or consumed by Gradle during the build, so secrets such as API keys, passwords, and tokens should stay in dedicated secret-management or environment-specific secure storage.

buildValue() is experimental. Opt in with @file:OptIn(io.kayan.gradle.ExperimentalKayanGradleApi::class) when using the Gradle build-time API from build.gradle.kts.

Build-time config access

When Gradle logic needs the same resolved config, use buildValue() directly in build.gradle.kts:

@file:OptIn(io.kayan.gradle.ExperimentalKayanGradleApi::class)

val isSearchEnabled =
    kayan.buildValue("feature_search_enabled")
        .asBoolean()

dependencies {
    if (isSearchEnabled) {
        implementation("com.example:search-sdk:1.0.0")
    }
}

This is useful for conditional dependencies, task inputs, and other configuration-time decisions. Provider variants such as asStringProvider() are also available for lazy task wiring.

Learn more

Development

Build everything:

./gradlew build

Run plugin tests:

./gradlew :gradle-plugin:test

Sample app

The sample app in sample/ demonstrates local plugin consumption, generated config usage, and Compose Multiplatform targets for desktop, web, and Apple platforms.

Run the desktop sample:

./gradlew -p sample run

Run the web sample:

./gradlew -p sample wasmJsBrowserDevelopmentRun

More sample details are in sample/README.md.

About

Kotlin Multiplatform Gradle plugin for generating typed BuildConfig-style objects from layered JSON configs.

Topics

Resources

Stars

Watchers

Forks

Releases

Used by

Contributors

Languages