com.rohittp.rentile:kmp
KMP integration guide
This guide takes you from an empty dependency declaration to PNG tile bytes. Along the way, it explains the two small adapters your app provides and the responsibilities Rentile deliberately leaves with you.
Step 1
Add the dependency
Rentile has one coordinate for common code. Your project also needs JetBrains' public Compose repository for the Skiko platform artifacts used by the renderer. The content filter keeps that repository limited to Skiko.
dependencyResolutionManagement {
repositories {
google()
mavenCentral()
maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") {
content { includeGroup("org.jetbrains.skiko") }
}
}
}
kotlin {
sourceSets {
commonMain.dependencies {
implementation("com.rohittp.rentile:kmp:<version>")
}
}
}
<version> with a published version. Until the first artifact is
available, use the source repository to evaluate the API rather than adding a
snapshot coordinate to an application build.
Step 2
Give Rentile transport and storage
Rentile does not force an HTTP client or database on your app. You provide a
ResourceTransport for fetching bytes and a RawResourceStore
for storing complete, validated responses. Both interfaces live in common Kotlin.
val configuration = RentileConfiguration(
transport = AppResourceTransport(httpClient),
rawResourceStore = AppRawResourceStore(cacheDirectory),
)
val rasterizer: BasemapRasterizer =
Rentile.create(configuration)
AppResourceTransport and AppRawResourceStore are example
names for adapters in your project. Rentile does not provide those classes.
Optional configuration
The defaults are enough to start. Add a CredentialProvider or
MapSessionProvider when your map provider needs them. You can also
tune concurrency and safety limits, or attach redacted diagnostic and metrics sinks.
| Option | Use it for |
|---|---|
credentialProvider | Credentials for exact HTTPS origins when the style does not already contain them |
sessionProvider | Short-lived provider session values |
executionPolicy | Bounds for exchanges, decoding, memory, and render workers |
resourceLimits | Hard ceilings for untrusted encoded and decoded resources |
diagnosticSink | Structured, sanitized renderer diagnostics |
metricsSink | Request, cache, decode, render, and output counters |
Step 3
Prepare a style, then render a batch
Preparation is deliberately split in two. First Rentile validates and compiles the style. Then it gathers everything required for a caller-defined tile batch. Once a batch is prepared, rendering performs no network or store access.
val style = rasterizer.prepare(
StyleInput.Remote("https://example.com/style.json")
)
val requestedTiles = listOf(
TileId(z = 12, x = 2203, y = 1345),
TileId(z = 12, x = 2204, y = 1345),
)
rasterizer.prepareBatch(style, requestedTiles).use { batch ->
// Content keys are available before drawing.
batch.contentKeys.forEach { (tile, key) ->
println("$tile will render as $key")
}
val result: RenderBatch = rasterizer.render(batch)
result.tiles.forEach { tile ->
savePng(tile.id, tile.pngBytes)
}
}
prepare
Checks one inline, remote, or prefetched style and returns a reusable PreparedStyle.
prepareBatch
Resolves raw resources and exposes stable content keys for the requested tiles.
render
Draws all or a selected subset of the prepared tiles and returns PNG bytes.
Choose a style input
| Input | When to use it |
|---|---|
StyleInput.Remote(url) | Let Rentile acquire the style through your transport adapter |
StyleInput.InlineJson(json, baseUri) | You already have JSON text; use baseUri for relative references |
StyleInput.Prefetched(bytes, identity, baseUri) | You already have bytes and a canonical, credential-free identity |
Output contract
A successful tile contains its TileId, encoded pngBytes,
the same contentKey exposed before drawing, and any sanitized diagnostics.
A render call is all-or-error: Rentile does not return a partial batch.
Host boundary
Keep your existing network stack
ResourceTransport is one bounded request/response exchange. Map its
request onto Ktor, OkHttp, URLSession, Curl, or another client already used by your
host. Return the decompressed body and the allowlisted response metadata.
fun interface ResourceTransport {
suspend fun execute(
request: TransportRequest
): TransportResponse
}
Requests include the URL, resource class, response-size limit, and a small set of cache-validation headers. Arbitrary headers do not cross the public boundary.
Credentials
If a style URL already contains provider credentials, Rentile uses them. Otherwise,
it asks your CredentialProvider only for the exact HTTPS origin and query
parameter it needs. Credentials and session values are kept out of logs, content
keys, cache paths, exception text, and diagnostics.
Caching
Two caches, two owners
Rentile stores reusable source material. Your app stores finished PNGs. Keeping those concerns separate lets your product choose the output lifetime and eviction policy that make sense for its workflows.
Rentile owns
- Complete encoded raw resources
- Compiled style and decoded-source memory
- In-process single-flight resource work
- Validation before a cache write is committed
Your app owns
- Rendered PNG storage and eviction
- Cross-process output coordination
- Priority, prefetching, and batch size
- Fallback output namespaces
Use PreparedBatch.contentKeys to look up rendered outputs before you call
render. A failed tile does not undo valid raw-resource entries acquired
for other work, and incomplete entries are never committed.
Failures
Handle facts, not exception messages
Every RentileException has a stable machine-readable code, a
pipeline stage, sanitized diagnostics, and affected tile identities.
Human-readable messages explain the problem but are not a stable API.
| Exception family | What it tells you |
|---|---|
StylePreparationException | The style is malformed or reaches a construct this profile cannot render |
ResourceAcquisitionException | A resource could not be fetched; status and retry delay are included when available |
ResourceDecodeException | Fetched bytes could not be decoded safely |
RasterizationException | Drawing failed for one or more affected tiles |
PngEncodingException | The rendered pixels could not be encoded as PNG |
SafetyLimitException | An encoded size, decoded size, dimension, or redirect ceiling was exceeded |
| Lifecycle exceptions | A renderer or batch is closed, foreign, or does not contain the requested tile |
Rentile does not retry or fall back on its own. Use the typed fields to decide
whether your app should retry, refetch, choose another renderer, or stop.
CancellationException is propagated unchanged.
Lifecycle
Close quickly, await when cleanup matters
BasemapRasterizer and PreparedBatch are common Kotlin
AutoCloseables. Calling close() is idempotent, non-blocking,
and non-throwing. It prevents new work and starts cancellation of owned jobs.
val rasterizer = Rentile.create(configuration)
try {
renderNeededTiles(rasterizer)
} finally {
rasterizer.close()
rasterizer.awaitClosed()
}
awaitClosed() suspends until workers, native objects, leases, and secret
state are released. Use it when teardown must be observed—for example, before a test
ends or a long-lived service replaces its renderer.
Renderer coverage
Check your style before committing
Rentile follows a versioned rentile-v1 compatibility profile validated
against an owner-controlled rolling style corpus. It does not promise support for every
current or future MapLibre Style Specification feature. A retained unsupported construct
fails during style preparation instead of producing a quietly incomplete map.
Implemented today
- Style version 8 validation
- Strict modern expressions, legacy functions, and legacy/modern filters required by the profile
- Zoom- and feature-evaluated backgrounds, fills, and lines
- Inline and TileJSON XYZ/TMS raster sources
- Raster paint controls and strict pixel-equivalent PNG pass-through
- Strict MVT decoding with bounded geometry and tag validation
- Inline and TileJSON vector sources with source bounds
- Vector overzoom through z22 with output-zoom style evaluation
- Profile fill/line paint, sprite patterns, dashes, gaps, offsets, blur, and translation
- Remote GeoJSON LineString sources through the common line renderer
- Raster DEM hillshade with bounded neighbor acquisition
- Flattened top-down extrusion footprints
- Independent RGBA/SDF point and repeated-line icons without glyph requests
- 256 px and 512 px PNG outputs
- Inline, remote, and prefetched style inputs
Not implemented yet
- Text, glyphs, shaping, and text-coupled icons
- Perspective extrusion and root terrain presentation
- Arbitrary future Style Specification behavior outside
rentile-v1 - Globe, fog, sky, and interactive camera state
Targets
Supported platforms
| Platform | KMP target | Execution model |
|---|---|---|
| Android arm64 | androidTarget | Background work; no View or Looper contract |
| Android x86_64 | androidTarget | Background work; emulator-compatible native runtime |
| iPhone and iPad | iosArm64 | Background work; no UIKit view |
| Apple Silicon Simulator | iosSimulatorArm64 | Background work |
| Linux x64 | linuxX64 | Headless native process |
| Linux arm64 | linuxArm64 | Headless native process |