<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>HarmonyOS on Trevor's Blog</title><link>http://mings.in/en/tags/harmonyos/</link><description>Recent content in HarmonyOS on Trevor's Blog</description><generator>Hugo</generator><language>en-US</language><lastBuildDate>Tue, 13 Oct 2026 09:12:18 +0800</lastBuildDate><atom:link href="http://mings.in/en/tags/harmonyos/index.xml" rel="self" type="application/rss+xml"/><item><title>Turning Any AI Agent into a HarmonyOS Workbench</title><link>http://mings.in/en/posts/harmony-ai/</link><pubDate>Tue, 13 Oct 2026 09:12:18 +0800</pubDate><guid>http://mings.in/en/posts/harmony-ai/</guid><description>A general-purpose agent writing ArkTS will crash and burn, for exactly three reasons. It does not know ArkTS decorators or build commands; it has no official docs to look things up in, so it invents APIs from memory; and even when the code is right, it has no idea how to build the package, install it on a device, and ship it to the app store.</description><content:encoded><![CDATA[<h2 id="the-problem-swapping-agents-will-not-fix">The problem swapping agents will not fix</h2>
<p>A general-purpose agent writing ArkTS will crash and burn, for exactly three reasons. It does not know ArkTS decorators or build commands; it has no official docs to look things up in, so it invents APIs from memory; and even when the code is right, it has no idea how to build the package, install it on a device, and ship it to the app store.</p>
<p>The holes can be plugged one at a time. Huawei and the community turned the plugging into tools you can bolt on externally, and going from nothing to usable is eight steps. This post walks them in order, with the command for each step and the output you should expect. At the end I wire all three pieces into a real agent, DeepSeek Harness, to see whether it actually buys it.</p>
<h2 id="four-things-to-install">Four things to install</h2>
<p>DevEco Studio, or its command line tools, is the foundation. Building, devices, signing, all the real work sits on it. @deveco/deveco-cli is the toolset, command name devecocli, and its job is installing capabilities into other agents.</p>
<p>The two MCPs do different jobs. The remote developer knowledge MCP handles doc lookup. The local deveco-mcp handles syntax checking for .ets and C/C++. The last piece is agc-cli, a community-written publishing CLI that drags AGC console operations back into the terminal.</p>
<p>Also get a Huawei developer account ready first. Installing the foundation and generating signatures both need it.</p>
<h2 id="step-one-install-the-foundation">Step one: install the foundation</h2>
<p>Go to the developer alliance download center (developer.huawei.com/consumer/cn/download/), log in, and pick the macOS build. On Apple Silicon you want the macOS ARM one.</p>
<p>Note the path after install. The default is /Applications/DevEco-Studio.app. You cannot skip this step, every real job devecocli does later depends on it.</p>
<p>If you do not want the whole IDE, the other path is the command line tools on the same page. Much smaller, but the official docs call one thing out:</p>
<blockquote>
<p>Set <code>DEVECO_CLI_CLT_PATH</code> to the Command Line Tools root when DevEco Studio is not installed; CLT is not discovered automatically from PATH or default installation directories.</p>
</blockquote>
<p>It will not be discovered from PATH or the default directory on its own. You have to point an environment variable at it by hand:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl"><span class="c1"># IDE installed</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DEVECO_CLI_STUDIO_PATH</span><span class="o">=</span>/Applications/DevEco-Studio.app
</span></span><span class="line"><span class="cl"><span class="c1"># command line tools only</span>
</span></span><span class="line"><span class="cl"><span class="nb">export</span> <span class="nv">DEVECO_CLI_CLT_PATH</span><span class="o">=</span><span class="nv">$HOME</span>/command-line-tools
</span></span></code></pre></div><p>Put those in ~/.zshrc to make them stick. On first launch Studio walks you through downloading the HarmonyOS SDK. That needs network and it is slow, so do not sit there waiting somewhere offline.</p>
<h2 id="step-two-install-the-devecocli-toolset">Step two: install the devecocli toolset</h2>
<p>Two npm packages together:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">npm install -g @deveco/deveco-cli @deveco/deveco-code
</span></span></code></pre></div><p>You get two commands. deveco is the Agent itself, customized from OpenCode; devecocli is the toolset. 255 dependencies between the two packages, 267 MB.</p>
<p>Check it before moving on:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli --help
</span></span><span class="line"><span class="cl">devecocli device list
</span></span></code></pre></div><p>The second one lists connected devices. If it says DevEco Studio cannot be found, the problem is not this command, go back and check that environment variable from step one.</p>
<h2 id="step-three-install-the-skill-pack-into-your-agent">Step three: install the skill pack into your agent</h2>
<p>This is the answer to the any agent half, one command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --skill --agent dsh,cursor,codex
</span></span></code></pre></div><p>Without &ndash;agent it installs into every host it recognizes. To install into a specific directory use &ndash;path, and add -f to overwrite existing config.</p>
<p>What lands looks like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">node_modules/@deveco/deveco-cli-common/SKILL.md
</span></span></code></pre></div><p>24,690 bytes, and the trigger conditions at the top are blunt:</p>
<blockquote>
<p>Use &lsquo;devecocli&rsquo; for HarmonyOS apps (scaffold, build, run, debug, devices, emulators, logs, docs, UI inspection, 鸿蒙开发文档, skills). Use it if workspace has build-profile.json5 or oh-package.json5, or user mentions HarmonyOS, 鸿蒙, DevEco, ArkTS, ArkUI &hellip;</p>
</blockquote>
<p>Once the agent reads that, hitting a HarmonyOS project or hearing the word HarmonyOS makes it think of devecocli on its own. The same package also carries an offline doc corpus. docs.zip is 40.5 MB, unpacking to 31,477 files, plus a 15.2 MB search index. That is what <code>devecocli docs search</code> queries, and it works without network.</p>
<p>Supported hosts are the official list: atomcode, codebuddy, claude-code, codex, cursor, dsh, deveco, opencode, pi, qoder, trae-cn. Eleven of them. For hosts outside the list there is a crude workaround: a skill pack is ultimately one Markdown file plus a doc corpus, so dropping it into that host&rsquo;s skill directory is the same thing.</p>
<p>To install in bulk or see what is already installed, use the skills subcommand:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli skills list
</span></span><span class="line"><span class="cl">devecocli skills add --all
</span></span></code></pre></div><h2 id="step-four-install-the-local-syntax-check-mcp">Step four: install the local syntax-check MCP</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --mcp
</span></span></code></pre></div><p>It configures deveco-mcp, a stdio MCP that exposes a single check tool, dedicated to syntax checking .ets, C and C++. Once it is wired into the agent, every round of edits gets self-checked before the agent moves on, instead of waiting for a full build to fail. The docs add one note: run it once per round of edits, not after every single edit.</p>
<p>One limitation from the docs: every supported host can configure global and project-level MCP, only qoder does not support project-level.</p>
<p>The service can be started by hand, which is useful when debugging:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli serve mcp
</span></span></code></pre></div><p>It honors a few environment variables: PROJECT_PATH, DEVECO_PATH, and NODE_MAX_OLD_SPACE_SIZE for the Node heap cap.</p>
<h2 id="step-five-connect-the-official-knowledge-mcp">Step five: connect the official knowledge MCP</h2>
<p>This is the most universal piece. Any client that supports MCP just gets a block in its config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;mcpServers&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;harmonyos_developer_knowledge&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;url&#34;</span><span class="p">:</span> <span class="s2">&#34;https://connect-api.cloud.huawei.com/api/developerknowledge/mcp&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">      <span class="nt">&#34;type&#34;</span><span class="p">:</span> <span class="s2">&#34;http&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>DevEco Code ships its own MCP management command, one line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">deveco mcp add harmonyos_developer_knowledge <span class="se">\
</span></span></span><span class="line"><span class="cl">  --url https://connect-api.cloud.huawei.com/api/developerknowledge/mcp
</span></span></code></pre></div><p>It reports status itself when done:</p>
<pre tabindex="0"><code>●  ✓ harmonyos_developer_knowledge connected
</code></pre><p>It only has two tools. searchDocuments searches by keyword and returns matching text blocks plus document IDs; getDocumentsById trades IDs for full text, ten documents max per call. Know the cost: about three thousand characters per search, thirty-five thousand per full document. Do not let it pull ten at once. The docs say this corpus syncs with the official site near real time, under 30 minutes of lag, and that is the most important difference between this path and letting the model answer from memory.</p>
<h2 id="deepseek-harness-as-the-example-how-three-pieces-land-in-a-real-agent">DeepSeek Harness as the example: how three pieces land in a real agent</h2>
<p>The steps above covered what to do. This section runs through dsh to show where it lands. It happens to be on that official host list, so all three pieces connect automatically.</p>
<p>The skill pack is one command, landing in dsh&rsquo;s own skills directory:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli init --skill --agent dsh
</span></span><span class="line"><span class="cl">ls ~/.dsh/skills
</span></span></code></pre></div><p>The directory structure is identical to its existing skills. One directory, one SKILL.md, two frontmatter lines at the top for name and description. This machine&rsquo;s ~/.dsh/skills already had seven skills starting with memorix-, and deveco-cli now sits alongside them.</p>
<p>MCP goes into dsh&rsquo;s config file cordis.patch.yml. Its config is layered profile patches, and MCP runs through its own mcp-client plugin, one record per service. The knowledge MCP goes in like this:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl">- <span class="nt">insert</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">mcp-harmonyos-knowledge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;@deepseek-ai/dsh-mcp-client&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">serverName</span><span class="p">:</span><span class="w"> </span><span class="l">harmonyos_developer_knowledge</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">transport</span><span class="p">:</span><span class="w"> </span><span class="l">streamable-http</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">url</span><span class="p">:</span><span class="w"> </span><span class="l">https://connect-api.cloud.huawei.com/api/developerknowledge/mcp</span><span class="w">
</span></span></span></code></pre></div><p>The local syntax-check MCP only swaps transport and the start command:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-yaml" data-lang="yaml"><span class="line"><span class="cl"><span class="w">    </span>- <span class="nt">id</span><span class="p">:</span><span class="w"> </span><span class="l">mcp-deveco-syntax</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">name</span><span class="p">:</span><span class="w"> </span><span class="s2">&#34;@deepseek-ai/dsh-mcp-client&#34;</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">      </span><span class="nt">config</span><span class="p">:</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">serverName</span><span class="p">:</span><span class="w"> </span><span class="l">deveco</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">transport</span><span class="p">:</span><span class="w"> </span><span class="l">stdio</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">command</span><span class="p">:</span><span class="w"> </span><span class="l">devecocli</span><span class="w">
</span></span></span><span class="line"><span class="cl"><span class="w">        </span><span class="nt">args</span><span class="p">:</span><span class="w"> </span><span class="p">[</span><span class="l">serve, mcp]</span><span class="w">
</span></span></span></code></pre></div><p>It only recognizes two transports. stdio is a locally started process, streamable-http is a server. A few defaults worth knowing: 60 second timeout per tool call, 32,768 bytes cap on instructions from the server, and auto-reconnect on disconnect, up to ten attempts.</p>
<p>After editing, do not restart and guess. dsh has a dump command that prints the whole merged config:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">dsh --profile &lt;your profile&gt; --patch ./hw-mcp.yml --dump-config
</span></span></code></pre></div><p>Once the config merges into the profile tree, the tool names really do show up in its tool table. I ran this with an isolated DSH_HOME:</p>
<pre tabindex="0"><code>mcp__harmonyos_developer_knowledge__searchDocuments
mcp__harmonyos_developer_knowledge__getDocumentsById
</code></pre><p>The naming rule is mcp, two underscores, service name, tool name, so several MCPs in the same agent will not collide. At this point, ask how to write a HarmonyOS interface inside dsh and it goes to the official docs instead of inventing something from memory.</p>
<p><img loading="lazy" src="/images/harmony-ai/fig-dsh.png"></p>
<h2 id="step-six-login-and-signing">Step six: login and signing</h2>
<p>To run on a real device you cannot skip this.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli auth login
</span></span></code></pre></div><p>It opens a browser for the Huawei developer account OAuth flow. Check login state with <code>devecocli auth status</code>, list joined team accounts with <code>devecocli auth team list</code>. Then generate a signature:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli signature generate --product default
</span></span></code></pre></div><p>The order is spelled out clearly: auth login, signature generate, build, run. Not one step can be skipped. Each missing step has its error: not logged in gives <code>Not logged in. Run devecocli auth login first</code>, an expired token gives <code>Invalid AccessToken. Sign in and try again</code>, a changed signature gives <code>error:install sign info inconsistent</code>, at which point add &ndash;uninstall to run, or &ndash;force when regenerating the signature.</p>
<h2 id="step-seven-install-agc-cli">Step seven: install agc-cli</h2>
<p>Publishing goes through AGC, which used to mean clicking through pages in a browser. This open source CLI turns the API into a searchable registry. In my test it had 159 endpoints across 13 families, each with method, path, required parameters, and a sourceUrl pointing back to Huawei&rsquo;s official docs.</p>
<p>The Homebrew route needs you to trust its tap first:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">brew tap createitv/tap
</span></span><span class="line"><span class="cl">brew trust createitv/tap
</span></span><span class="line"><span class="cl">brew install agc-cli
</span></span></code></pre></div><p>Or download the prebuilt binary from the official Release. There is a darwin_arm64 package, so verify the hash before using it:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">shasum -a <span class="m">256</span> agc-cli_0.2.0_darwin_arm64.tar.gz
</span></span><span class="line"><span class="cl">./agc version
</span></span></code></pre></div><p>Three ways to configure credentials, pick one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">agc auth login --client-id &lt;id&gt; --client-key &lt;key&gt;
</span></span><span class="line"><span class="cl">agc auth login --api-client-file client.json
</span></span><span class="line"><span class="cl">agc auth login --service-account-file sa.json
</span></span></code></pre></div><p><code>agc auth check</code> shows which one is active, <code>agc auth list</code> lists all stored profiles, <code>agc auth token</code> signs a temporary token for the current one.</p>
<p>By default it only rehearses. Before anything really goes out you see what the request looks like:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">agc publishing app-info-query --invoke --query <span class="nv">appId</span><span class="o">=</span><span class="m">123456789</span>
</span></span><span class="line"><span class="cl"><span class="o">{</span><span class="s2">&#34;data&#34;</span>:<span class="o">{</span><span class="s2">&#34;method&#34;</span>:<span class="s2">&#34;GET&#34;</span>,<span class="s2">&#34;url&#34;</span>:<span class="s2">&#34;https://connect-api.cloud.huawei.com/api/publish/v2/app-info?appId=123456789&amp;lang=zh-CN&#34;</span>,<span class="s2">&#34;dryRun&#34;</span>:true<span class="o">}}</span>
</span></span></code></pre></div><p>Once you are happy, explicitly add &ndash;dry-run=false to actually send it. Each endpoint&rsquo;s response also carries an affordances block listing which command you can run next. The agent does not need to memorize the command tree, it just follows the response.</p>
<h2 id="step-eight-run-it-and-watch-the-agent-check-its-own-work">Step eight: run it and watch the agent check its own work</h2>
<p>With all this installed, project to device is a straight line:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli build
</span></span><span class="line"><span class="cl">devecocli emulator list
</span></span><span class="line"><span class="cl">devecocli emulator start <span class="s2">&#34;Phone&#34;</span>
</span></span><span class="line"><span class="cl">devecocli run
</span></span></code></pre></div><p>The first emulator use needs a license, interactive with <code>devecocli emulator license</code>, which requires a TTY, or <code>devecocli emulator license accept</code> in scripts. If the image download fails, do not retry in a loop. The docs say plainly to hand that one to the user.</p>
<p>After the run, look at the two directories that appeared in the project. <code>.verify</code> holds verification artifacts, <code>docs/screenshots</code> holds screenshots, and the latter were produced automatically:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">docs/screenshots
</span></span><span class="line"><span class="cl">  01-home.png                          home
</span></span><span class="line"><span class="cl">  03-quick-entry.png                   quick entry
</span></span><span class="line"><span class="cl">  05-stats-mpchart.png                 stats charts
</span></span><span class="line"><span class="cl">  06-stats-retry.png                   retry after stats failure
</span></span><span class="line"><span class="cl">  08-trip-edit.png                     trip editing
</span></span><span class="line"><span class="cl">  09-home-empty.png                    empty state
</span></span><span class="line"><span class="cl">  10-real-device-home.png              real device
</span></span><span class="line"><span class="cl">  11-real-device-trip-detail-finish.png
</span></span><span class="line"><span class="cl">  12-light-home.png                    light theme
</span></span><span class="line"><span class="cl">  15-dark-home.png                     dark theme
</span></span><span class="line"><span class="cl">  19-dark-to-system-fixed.png
</span></span></code></pre></div><p>Numbers run from 01 to 19. Two of them are real device screenshots, meaning it installed the package on a device and actually ran it. Light and dark each get a set, meaning theme switching is in its flow too. There are empty states and the retry state after a stats failure, meaning it is not satisfied with the happy path and lays out the corner cases one by one. None of these were taken by hand.</p>
<p>The point of this step is the loop closing. Before, the agent handed you code and you had to look at whether it was any good. Now it can see the result itself, screenshots land as files, and the next round of edits has something to go on. This is also why I would file devecocli under tool rather than client: it is not the hand that writes code for you, it is the eye that lets the agent check its own work.</p>
<p><img loading="lazy" src="/images/harmony-ai/fig-workbench.png"></p>
<h2 id="one-verification-command-per-step">One verification command per step</h2>
<p>Do not go by feel afterwards, walk through them one by one:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-bash" data-lang="bash"><span class="line"><span class="cl">devecocli --help                  <span class="c1"># toolset is there</span>
</span></span><span class="line"><span class="cl">devecocli device list             <span class="c1"># devices are found</span>
</span></span><span class="line"><span class="cl">devecocli docs search ArkTS       <span class="c1"># offline docs are there</span>
</span></span><span class="line"><span class="cl">devecocli skills list             <span class="c1"># skill pack installed</span>
</span></span><span class="line"><span class="cl">deveco mcp list                   <span class="c1"># knowledge MCP connected</span>
</span></span><span class="line"><span class="cl">devecocli auth status             <span class="c1"># login state</span>
</span></span><span class="line"><span class="cl">agc auth check                    <span class="c1"># AGC credentials configured</span>
</span></span><span class="line"><span class="cl">agc endpoints                     <span class="c1"># registry readable</span>
</span></span></code></pre></div><p>Whichever one fails, the problem is in that step, and you do not have to hunt around.</p>
<h2 id="a-bit-of-judgment">A bit of judgment</h2>
<p>Of the four things, which would I install first? The foundation, because everything else is blocked on it. But if you only want to spend twenty minutes testing the water, connect the remote knowledge MCP first: one URL, no device, no account, and it plugs the most fatal hole.</p>
<p>Do not reverse the order. The skill pack and local MCP both hang off devecocli, and devecocli&rsquo;s real work hangs off DevEco Studio. With a weak foundation, every later step hits the same sentence.</p>
<p>Do you let your everyday agent write HarmonyOS code? Where does it crash most often? Tell me in the comments.</p>
]]></content:encoded></item><item><title>Kotlin 2.4 Is Here and HarmonyOS Is Adapting to 2.4: Two Fronts for KMP</title><link>http://mings.in/en/posts/kmp-24-ohos/</link><pubDate>Fri, 18 Sep 2026 12:34:38 +0800</pubDate><guid>http://mings.in/en/posts/kmp-24-ohos/</guid><description>A while back I wrote a piece on HarmonyOS and KMP, and at the time I found one key fact: the stable release the community published was HarmonyOS support built on KMP 2.2.21 / CMP 1.9.2.</description><content:encoded><![CDATA[<h2 id="one-branch-name-gives-away-what-is-happening">One branch name gives away what is happening</h2>
<p>A while back I wrote a piece on HarmonyOS and KMP, and at the time I found one key fact: the stable release the community published was HarmonyOS support built on <strong>KMP 2.2.21 / CMP 1.9.2</strong>.</p>
<p>Going back to that repo the other day, I found one extra line in the branch list:</p>
<blockquote>
<p><strong>develop-2.4</strong>
Documentation set for framework KMP <strong>2.4.21</strong> _ CMP <strong>1.12.0</strong></p>
</blockquote>
<p>And the branch <strong>was updated 21 hours ago</strong>.</p>
<p>There is another one:</p>
<blockquote>
<p><strong>main-2.2</strong> (default branch)
Documentation set for framework KMP <strong>2.2.21</strong> _ CMP <strong>1.9.2</strong></p>
</blockquote>
<p>On one side, the stable release already shipped. On the other, the new baseline being pushed forward.</p>
<p><strong>On the HarmonyOS front, KMP is starting to line up with the latest upstream version.</strong></p>
<p>And at almost the same moment, upstream JetBrains had just released Kotlin 2.4.20.</p>
<p>Two tracks, colliding at one point in time.</p>
<h2 id="first-the-upstream-track-what-changed-in-kotlin-2420">First, the upstream track: what changed in Kotlin 2.4.20</h2>
<p>If you only skim the changelog you might think, oh, another patch-and-polish release — of the two hundred-odd changes, most are bugfixes.</p>
<p>But a few of them land hard for <strong>people who write KMP</strong>.</p>
<p><strong>One: Swift interop can finally hold its own.</strong></p>
<p>Swift export now supports mapping Kotlin&rsquo;s <code>sealed class</code> straight to a Swift <code>enum</code>:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-swift" data-lang="swift"><span class="line"><span class="cl"><span class="c1">// no more default</span>
</span></span><span class="line"><span class="cl"><span class="kd">let</span> <span class="nv">name</span> <span class="p">=</span> <span class="k">switch</span> <span class="n">shape</span><span class="p">.</span><span class="n">sealedType</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="p">.</span><span class="n">circle</span><span class="p">(</span><span class="kd">let</span> <span class="nv">t</span><span class="p">):</span>    <span class="s">&#34;圆&#34;</span>
</span></span><span class="line"><span class="cl"><span class="k">case</span> <span class="p">.</span><span class="n">rectangle</span><span class="p">(</span><span class="kd">let</span> <span class="nv">t</span><span class="p">):</span> <span class="s">&#34;方&#34;</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>   <span class="c1">// ← exhaustiveness check</span>
</span></span></code></pre></div><p>Handling this kind of polymorphic type used to mean either a pile of <code>if let</code> on the Swift side, or being forced to add a <code>default</code> branch that can never run — <strong>which kills exhaustiveness checking outright</strong>. Now the compiler catches the gaps for you.</p>
<p>There is also <strong>cross-language inheritance</strong>: a Kotlin type can inherit from a Swift class or protocol (including pure Swift libraries), with no wrapper layer needed.</p>
<p>Both point the same direction: <strong>the interop friction KMP has always been criticized for on iOS is being sanded down, bit by bit.</strong></p>
<p><strong>Two: klib incremental compilation hits Beta.</strong></p>
<pre tabindex="0"><code>kotlin.incremental.native=true
</code></pre><p>Change one line of Kotlin and you no longer wait for the whole native module to rebuild. If you have done Kotlin/Native, you know what that does for the day-to-day.</p>
<p><strong>Three: the Kotlin/JS test toolchain moved from Karma to Playwright</strong> — about time.</p>
<p><strong>So the theme of the upstream track is: smoothing out the seams between languages.</strong></p>
<h2 id="now-the-harmonyos-track-where-it-actually-stands">Now the HarmonyOS track: where it actually stands</h2>
<p>To understand the HarmonyOS side you have to start with its <strong>version numbering scheme</strong>, written in the stable release announcement that went out yesterday:</p>
<blockquote>
<p>Version numbers use &ldquo;<strong>upstream baseline + HarmonyOS adaptation sequence</strong>&rdquo;: 2.2.21-1.0.0 / 1.9.2-1.0.0 means a HarmonyOS adaptation stable release based on upstream KMP 2.2.21 and CMP 1.9.2.</p>
</blockquote>
<p>That rule matters. It says one thing:</p>
<blockquote>
<p><strong>HarmonyOS support is not a separate stove. It is chasing upstream versions.</strong></p>
</blockquote>
<p>Which is why you see numbers like <code>2.2.21-1.0.0</code> — the <code>2.2.21</code> in front is the upstream Kotlin version it corresponds to, and the <code>1.0.0</code> behind is the HarmonyOS adaptation&rsquo;s own iteration number.</p>
<p>Already published to Maven:</p>
<table>
	<thead>
			<tr>
					<th>Component</th>
					<th>Version</th>
					<th>Status</th>
			</tr>
	</thead>
	<tbody>
			<tr>
					<td>kotlin</td>
					<td>2.2.21-1.0.0</td>
					<td>Published to Maven</td>
			</tr>
			<tr>
					<td>compose-multiplatform</td>
					<td>1.9.2-1.0.0</td>
					<td>Published to Maven</td>
			</tr>
			<tr>
					<td>compose-multiplatform-core</td>
					<td>1.9.2-1.0.0</td>
					<td>Published to Maven</td>
			</tr>
			<tr>
					<td>skiko</td>
					<td>0.9.22.2-1.0.0</td>
					<td>Published to Maven</td>
			</tr>
			<tr>
					<td>akinterop</td>
					<td>1.0.1</td>
					<td>Published to Maven</td>
			</tr>
			<tr>
					<td>KMP OHOS Support</td>
					<td>1.0.0</td>
					<td>Published to the plugin marketplace</td>
			</tr>
	</tbody>
</table>
<p>And the new <code>develop-2.4</code> branch targets a baseline of <strong>KMP 2.4.21 / CMP 1.12.0</strong>.</p>
<p>The development cadence announcement says it too:</p>
<blockquote>
<p><strong>develop</strong> branch: used for monthly development-state code merges, <strong>merged into main after each monthly release gate</strong>.</p>
</blockquote>
<p>In plain language: <strong>one version forward every month, merged into the stable line only once it is ready.</strong> Not shipping on a whim, but a rolling cadence.</p>
<h2 id="what-that-branch-has-been-busy-with">What that branch has been busy with</h2>
<p>I went through the <code>develop-2.4</code> commit history. The recent activity is interesting.</p>
<p>The latest one adapts the <strong>CAPI user manual to API 26</strong> — bumping the OHOS CAPI version to API26, updating &ldquo;modules, minimum API, and typical dependency libraries&rdquo; to match, and adding a whole section on <strong>&ldquo;special changes caused by the CAPI upgrade to API 26.&rdquo;</strong></p>
<p>Another one updates <code>akinterop</code> to 1.0.1 and <code>sqldelight</code> to 2.2.1-1.1.0.</p>
<p><strong>Those two directions map exactly to the two real blockers for HarmonyOS cross-platform work:</strong></p>
<ul>
<li><strong>CAPI follows the HarmonyOS system version</strong> — the system upgrades, the bridge layer has to upgrade with it. That is the &ldquo;do not fall behind&rdquo; problem</li>
<li><strong>Adapted version numbers for third-party libraries</strong> — whether the ecosystem libraries keep up decides whether a project dares to use this at all</li>
</ul>
<h2 id="while-we-are-here-what-cmp-112-brings-upstream">While we are here: what CMP 1.12 brings upstream</h2>
<p>Since <code>develop-2.4</code> is tracking CMP 1.12.0, what changed in 1.12 upstream is worth a look too.</p>
<p>CMP 1.12.0 shipped on August 25, and the highlight is on the Desktop side:</p>
<blockquote>
<p>MCP server was introduced for Compose Hot Reload that enables <strong>AI agents to interact with a running Compose application in real time</strong></p>
</blockquote>
<p>In plain language: <strong>Compose hot reload got an MCP server, so an AI agent can interact with a running Compose app in real time.</strong></p>
<p>That one is subtle. The intersection of Compose and AI is moving from &ldquo;use AI to write Compose code&rdquo; toward &ldquo;<strong>let AI operate the running app directly</strong>.&rdquo;</p>
<p>It also brings a batch of breaking changes, so watch out when migrating:</p>
<ul>
<li>The deprecation level on typealiases like <code>NativeCanvas</code> and <code>NativePaint</code> was raised to <strong>ERROR</strong></li>
<li>The <code>background</code> parameter on <code>SwingPanel</code> is deprecated</li>
</ul>
<p><strong>Jumping from 1.9.2 to 1.12.0 is not just a version number change — migration cost counts too.</strong></p>
<h2 id="what-the-two-things-together-tell-you">What the two things together tell you</h2>
<p>Put the two tracks side by side and the picture is fairly clear:</p>
<p><strong>KMP&rsquo;s &ldquo;one codebase, many platforms&rdquo; is being pushed forward on several fronts at once.</strong></p>
<ul>
<li><strong>Upstream</strong> is polishing cross-language interop (Swift export, incremental compilation)</li>
<li><strong>The HarmonyOS community track</strong> is chasing upstream versions (2.2 → 2.4), filling in system support (CAPI → API 26), and widening the third-party library ecosystem</li>
</ul>
<p>They are not the same team, but they are heading in <strong>the same direction</strong> — making &ldquo;write Kotlin once, run on more platforms&rdquo; a little less friction.</p>
<p>For developers, the test is simple:</p>
<blockquote>
<p><strong>When you evaluate a new platform, the question is no longer &ldquo;does it support this&rdquo; but &ldquo;which upstream version has it caught up to, are the libraries enough, does the performance hold up.&rdquo;</strong></p>
</blockquote>
<p><strong>From &ldquo;can it run&rdquo; to &ldquo;can it keep up&rdquo; — that shift is itself a new stage.</strong></p>
<h2 id="but-the-communitys-real-voice-is-worth-more-than-the-announcement">But the community&rsquo;s real voice is worth more than the announcement</h2>
<p>Announcements are always pretty. I went and read the community discussion forum on purpose, because that is where this track&rsquo;s actual temperature is.</p>
<p><strong>The hottest thread asks &ldquo;can this be merged into the official JetBrains branch soon&rdquo;</strong> (started 30 days ago, 7 replies).</p>
<p>The poster was direct:</p>
<blockquote>
<p>Adding HarmonyOS to my existing pure KMP &amp; CMP app project <strong>runs into a large number of unsupported libraries</strong>, and during compilation several already-adapted libraries show internal dependency conflicts. I hope it can be merged upstream soon so library developers across the whole KMP community can add ohos versions, <strong>the CPF community&rsquo;s progress on library support is far too slow</strong>.</p>
</blockquote>
<p>He named three libraries specifically: <code>androidx-datastore</code>, <code>androidx-room3</code>, <code>androidx-sqlite</code> — &ldquo;these three are basically the foundational dependency libraries for the persistence approaches everyone uses.&rdquo;</p>
<p>Then the part that stings:</p>
<blockquote>
<p>None of them support ohos <strong>x86</strong>. On Windows a developer can barely build and test an app with normal functionality — at minimum you have to keep a HarmonyOS phone plugged in as the test target&hellip;</p>
</blockquote>
<p>He also gave a concrete trap: a <code>room3</code> alpha pulled from some mirror repo generates code that errors on a missing <code>androidx.sqlite</code> implementation, while upstream <code>room3 3.0.1</code> works fine.</p>
<p><strong>The official reply came, and it was honest:</strong></p>
<blockquote>
<p>Hello, <strong>support for ohos x86 is already in planning</strong>, thanks for your interest.</p>
</blockquote>
<p>Other threads are each real in their own way:</p>
<ul>
<li><strong>&ldquo;Under skia rendering the page frame production is dragged to about one frame per 3 seconds, suspected abnormality in the VsyncOnDemand deferred deregistration mechanism&rdquo;</strong> — performance</li>
<li><strong>&ldquo;material3 blocks porting an existing project&rdquo;</strong> — component library traps</li>
<li><strong>&ldquo;Is webView supported right now?&rdquo;</strong> — capability coverage</li>
<li><strong>&ldquo;Has anyone actually shipped a project? What traps did you hit&rdquo;</strong> — and someone replied with: <strong>&ldquo;currently doing a KMP adaptation of a voice assistant&rdquo;</strong></li>
</ul>
<p><strong>Add these up and you have the real state of this track: it runs, people are using it, but the ecosystem and performance are still catching up.</strong></p>
<h2 id="why-following-upstream-is-slow-work">Why &ldquo;following upstream&rdquo; is slow work</h2>
<p>Seeing that &ldquo;progress is far too slow&rdquo; line, some people will assume it is a matter of insufficient investment.</p>
<p>But there is a technical reality worth spelling out: <strong>HarmonyOS support changes the compiler backend. It is not writing a plugin.</strong></p>
<p>Go through the CPF-KMP-CMP repo list and you see these names:</p>
<p><code>kotlin</code>, <code>compose-multiplatform</code>, <code>compose-multiplatform-core</code>, <code>skiko</code>, <code>akinterop</code>, <code>cicd-toolkit</code>…</p>
<p><strong>These are HarmonyOS forks of the Kotlin compiler, the Compose framework, and the graphics rendering layer.</strong></p>
<p>Which means the work includes at least:</p>
<ul>
<li>Adding a new <strong>compilation target</strong> to Kotlin/Native</li>
<li>Wiring Compose&rsquo;s rendering pipeline into HarmonyOS&rsquo;s <strong>Render Service</strong></li>
<li>Getting the Skia graphics library that Skiko wraps to run on HarmonyOS</li>
</ul>
<p><strong>And every time upstream ships a new version, all of those changes have to be re-aligned.</strong></p>
<p>That is what the &ldquo;upstream baseline + adaptation sequence&rdquo; version scheme really means — <strong>it describes the nature of the work precisely: continuous tracking, not a one-time port.</strong></p>
<p>Look again at &ldquo;monthly development-state merges, merged into main after each monthly release gate&rdquo; and it reads differently:</p>
<p><strong>This is not slow. This is doing a very heavy thing at a pace that can be sustained.</strong></p>
<h2 id="a-few-of-my-own-views">A few of my own views</h2>
<p><strong>One: the essence of &ldquo;adaptation&rdquo; here is following upstream, not reinventing the wheel.</strong></p>
<p>You can see it in the version scheme alone (upstream baseline + adaptation sequence). The core work on the HarmonyOS track is getting upstream&rsquo;s compiler and UI framework running on HarmonyOS, not inventing a separate API.</p>
<p><strong>Which means its ceiling is tied to upstream</strong>: upstream smooths out Swift interop and that is a dividend for iOS; upstream improves the compiler and HarmonyOS only gets it by keeping up.</p>
<p><strong>Two: the three community complaints point at the same problem.</strong></p>
<p>x86 support, third-party library versions, performance — three things on the surface, all <strong>&ldquo;ecosystem maturity&rdquo;</strong> underneath.</p>
<p>And ecosystem maturity has a cruel property: <strong>it cannot be solved with enthusiasm, only with time and real projects wearing it in.</strong> The official reply of &ldquo;already in planning&rdquo; is true, and there is a certain helplessness in how true it is.</p>
<p><strong>Three: if you are evaluating whether to get on this track, here is a framework.</strong></p>
<p>Do not ask &ldquo;does it support HarmonyOS&rdquo; — <strong>ask these three questions</strong>:</p>
<ol>
<li><strong>Which upstream version has it caught up to?</strong> (right now 2.2.21 is stable, 2.4.21 is in development)</li>
<li><strong>Have the libraries I depend on been adapted?</strong> (especially foundational ones like persistence and networking — <code>room3</code> and <code>datastore</code> are the acknowledged pain points)</li>
<li><strong>What is my dev machine?</strong> (if you need an x86 emulator, you still have to wait; an Apple silicon Mac plus a real device is the steadier combination)</li>
</ol>
<p><strong>All three clear, then start. One blocked, and the plan goes in a note for later.</strong></p>
<h2 id="last">Last</h2>
<p>Kotlin 2.4 and HarmonyOS 2.4 look like two unrelated things.</p>
<p>But they are answering the same question:</p>
<blockquote>
<p><strong>One body of Kotlin code: how many places can it run, and how well?</strong></p>
</blockquote>
<p>Upstream is sanding down the seams between languages, and the HarmonyOS track is widening the map.</p>
<p><strong>Both are making that number bigger.</strong></p>
<hr>
<p>I want to ask you something:</p>
<p><strong>Would &ldquo;HarmonyOS support has caught up to 2.4&rdquo; make you reconsider a KMP project you have on your desk?</strong></p>
<p><strong>Or have you already been through the traps on this track?</strong> Talk in the comments — I would genuinely like to hear from real projects.</p>
<hr>
<p><em>References: CPF-KMP-CMP community repos (AtomGit/GitCode) branch and release announcements, public posts in the community discussion forum, JetBrains Kotlin 2.4.20 release notes.</em></p>
]]></content:encoded></item><item><title>KMP Lands on HarmonyOS: One Compose Codebase, Four Platforms Now</title><link>http://mings.in/en/posts/kmp-harmony/</link><pubDate>Wed, 16 Sep 2026 21:26:40 +0800</pubDate><guid>http://mings.in/en/posts/kmp-harmony/</guid><description>Start with a number: four.</description><content:encoded><![CDATA[<h2 id="a-quiet-bit-of-map-expansion">A quiet bit of &ldquo;map expansion&rdquo;</h2>
<p>Start with a number: <strong>four</strong>.</p>
<p>Android, iOS, desktop — that&rsquo;s the main ground Kotlin Multiplatform has covered steadily for years (Web / Wasm is still in progress). Write one set of shared logic, add one Compose UI, and several platforms run together.</p>
<p>And yesterday, that list gained a fourth member: <strong>HarmonyOS</strong>.</p>
<p>And this time it isn&rsquo;t &ldquo;theoretically runs&rdquo; — it&rsquo;s an <strong>official release</strong>. Components are in the Maven repo, the toolchain plugin is searchable in the Android Studio plugin marketplace, and the docs are on AtomGit.</p>
<p>What surprised me most was the publisher&rsquo;s line:</p>
<blockquote>
<p>Teams already using CMP on other platforms can reuse the same Compose code for UI and business logic directly, without writing a whole separate ArkUI implementation for HarmonyOS.</p>
</blockquote>
<p><strong>No ArkUI rewrite.</strong> For anyone doing cross-platform, those few words carry real weight.</p>
<h2 id="lets-get-the-facts-straight">Let&rsquo;s get the facts straight</h2>
<p>What shipped is the <strong>first HarmonyOS release of KMP&amp;CMP</strong>, on September 15, 2026.</p>
<p>The key facts:</p>
<ul>
<li><strong>Who made it</strong>: built jointly by community partners in the CPF-KMP-CMP SIG (note: a community SIG, not JetBrains official — I&rsquo;ll get into that distinction later)</li>
<li><strong>Based on</strong>: upstream <strong>KMP 2.2.21 / CMP 1.9.2</strong></li>
<li><strong>Goal</strong>: bring the &ldquo;shared logic, native UI rendering&rdquo; paradigm to HarmonyOS</li>
<li><strong>How to get it</strong>: components are published to the Maven repo; the toolchain plugin <strong>KMP OHOS Support</strong> is searchable and installable from the Android Studio plugin marketplace</li>
</ul>
<p>The official positioning on who it&rsquo;s for is clear too:</p>
<blockquote>
<p>For teams already using KMP/CMP for cross-platform work, this release helps lower the cost of moving to HarmonyOS. HarmonyOS teams can also use it for mixed Kotlin and ArkTS development, and to widen their technology options.</p>
</blockquote>
<p>Translated: <strong>KMP teams who want HarmonyOS now face a lower cost; HarmonyOS teams who want Kotlin now have a path.</strong></p>
<p>Some background while we&rsquo;re here — this has been building for three months. The community edition opened Beta in June, and in August InfoQ reported a round of measurements (95% less rendering memory, 90% fewer GC jank incidents). Now it&rsquo;s properly landed.</p>
<h2 id="highlight-one-compose-runs-on-harmonyos-directly-no-ui-rewrite">Highlight one: Compose runs on HarmonyOS directly, no UI rewrite</h2>
<p>This is the one I think is most valuable.</p>
<p>On HarmonyOS, the official route is <strong>ArkTS + ArkUI</strong>. For a team already doing KMP/CMP, that means the HarmonyOS side needs <strong>its own separate UI</strong> — business logic shared, but the interface rewritten.</p>
<p>This release paves that road:</p>
<blockquote>
<p>CPF-KMP-CMP provides platform implementations of compose.ui, compose.foundation, compose.material and compose.material3 on the HarmonyOS side, so you can keep the familiar declarative UI habits while building HarmonyOS screens.</p>
</blockquote>
<p><strong>compose.material3 is there.</strong> Which is to say, the Compose code you&rsquo;re used to writing is recognized on HarmonyOS.</p>
<p>For a project already running CMP on Android/iOS, that means <strong>the cost of adding HarmonyOS drops from &ldquo;rewrite a UI&rdquo; to &ldquo;add a target&rdquo;</strong>.</p>
<p>How big a difference is that? Anyone who&rsquo;s done cross-platform knows.</p>
<h2 id="highlight-two-kotlin--arkts-two-way-interop">Highlight two: Kotlin ↔ ArkTS two-way interop</h2>
<p>The second one matters a lot for existing projects.</p>
<p>The reality is: plenty of HarmonyOS projects <strong>already have a pile of ArkTS</strong> (system capabilities, business modules), and ripping it all out for Kotlin isn&rsquo;t an option.</p>
<p>The answer here is <strong>akinterop</strong>:</p>
<blockquote>
<p>Shared logic can keep being written in Kotlin. When the system side or existing modules stay in ArkTS, use akinterop for two-way interop: declare intent with annotations / decorators, let the toolchain generate the bridge, and write less C glue. Primitive types, containers and exceptions can cross sides, existing projects can adopt incrementally, and nothing has to be torn down for interop.</p>
</blockquote>
<p><strong>&ldquo;Adopt incrementally, nothing torn down&rdquo;</strong> — that line is the reassurance existing projects needed.</p>
<p>Annotation declares intent, toolchain generates the bridge — a far better experience than hand-writing C glue. Anyone who&rsquo;s written JNI knows that pain.</p>
<h2 id="highlight-three-unified-rendering-this-is-the-technical-core">Highlight three: unified rendering (this is the technical core)</h2>
<p>This one&rsquo;s more hardcore, but I think it&rsquo;s the most technically substantial piece of the whole thing.</p>
<p>The problem first: <strong>traditional cross-platform UI approaches usually build their own rendering pipeline.</strong></p>
<p>What&rsquo;s wrong with building your own? The report says it directly:</p>
<blockquote>
<p>Traditional cross-platform UI solutions often build their own rendering pipeline, which tends to add initialization and composition overhead, and often shows up as slow startup and animations that don&rsquo;t match the system.</p>
</blockquote>
<p>Self-rendering means your UI doesn&rsquo;t go down the system&rsquo;s path — startup needs an extra init step, animation rhythm doesn&rsquo;t line up with the system, and it just looks &ldquo;not native&rdquo;.</p>
<p>This approach goes <strong>the other way</strong>:</p>
<blockquote>
<p>CPF-KMP-CMP connects Compose&rsquo;s drawing capability to HarmonyOS&rsquo;s Render Service / RenderNode system rendering, with HarmonyOS handling composition and refresh scheduling uniformly (unified rendering requires API 19 or above).</p>
</blockquote>
<p><strong>Handing drawing over to HarmonyOS to composite itself</strong> pays off in a chain:</p>
<ul>
<li><strong>Faster first frame</strong>: no self-rendering init wait, so startup and navigation feel snappier</li>
<li><strong>Fine-grained partial refresh</strong>: update only when state changes, with partial recording support — <strong>only the changed region is repainted</strong></li>
<li><strong>Native component mixing</strong>: Compose screens can mix with ArkUI and C-API, so native capabilities like maps and video are easier to embed</li>
<li><strong>System-aligned animation</strong>: frame rhythm follows the system&rsquo;s DisplaySync, so animation matches native HarmonyOS apps</li>
</ul>
<p>That last one matters especially. The reason cross-platform apps look fake at a glance often isn&rsquo;t wrong layout — it&rsquo;s that <strong>the animation rhythm is wrong</strong>, frame scheduling off the system&rsquo;s beat.</p>
<blockquote>
<p>⚠️ Note one limitation: <strong>unified rendering requires API 19 or above</strong>. On older devices it degrades.</p>
</blockquote>
<h2 id="the-performance-account-20-off-compile-time-30-off-heap">The performance account: 20% off compile time, 30% off heap</h2>
<p>They gave some genuinely specific numbers this time, unlike the vague claims that usually pass for &ldquo;performance optimization&rdquo;:</p>
<p><strong>Compile side (parallel compilation):</strong></p>
<blockquote>
<p>Splitting LLVM IR and running local optimization across threads before merging shortens compile waits on large projects; typical scenarios save roughly 20%+ of the time.</p>
</blockquote>
<p><strong>Memory side (CommonGC):</strong></p>
<blockquote>
<p>Using a concurrent mark-copy collection algorithm to reduce memory fragmentation and GC pause time, KN heap usage in typical scenarios drops by about 30%.</p>
</blockquote>
<p>There are also two engineering optimizations that are genuinely practical:</p>
<ul>
<li><strong>Modular splitting</strong>: a single KMP project can be configured into multiple <code>.so</code> files, and combined with distributed compilation it shortens builds further</li>
<li><strong>Debug incremental compilation</strong>: on macOS / Linux, incremental compilation based on a static library cache — <strong>only the changed part recompiles, no full relink every time</strong></li>
</ul>
<p>(Anyone doing KMP probably has a small stress reaction to the words &ldquo;full relink&rdquo;)</p>
<p>One more detail I like — <strong>no idle spinning in the background</strong>:</p>
<blockquote>
<p>The unified rendering path supports requesting VSync signals on demand, actively suppressing refresh drawing when page content hasn&rsquo;t changed; it also supports LTPO hardware characteristics, stopping pointless draw loops automatically once the app goes to the background&hellip; This is framework default behavior, requiring no extra code from the app.</p>
</blockquote>
<p><strong>Default behavior, no app code needed.</strong> This kind of &ldquo;the framework worries about it for you&rdquo; design is far friendlier than making you turn animations off by hand in <code>onPause</code>.</p>
<h2 id="getting-started-three-steps">Getting started: three steps</h2>
<p>The flow is simpler than I expected:</p>
<blockquote>
<p>(1) DevEco Studio: use 6.0.0+ to download HarmonyOS SDK API 17+, open the harmonyApp in the project and set up the debug signature.
(2) Android Studio: write KMP&amp;CMP code day to day.
(3) Android Studio: pick a device and harmonyApp, hit Run (keep DevEco running while pushing the package).</p>
</blockquote>
<p><strong>Note the parenthesis in step 3</strong> — DevEco Studio has to stay open while pushing. That kind of &ldquo;two IDEs cooperating&rdquo; detail is exactly where people trip, and they said it up front.</p>
<p>The docs are on <strong>AtomGit</strong> (search <code>CPF-KMP-CMP/docs</code>) if you want to dig in.</p>
<h2 id="why-this-piece-of-the-harmonyos-puzzle-matters-so-much">Why this piece of the HarmonyOS puzzle matters so much</h2>
<p>Honestly: for KMP developers in China, this puzzle piece may matter far more than Web/Wasm.</p>
<p>The reason is practical — <strong>for mobile work in China, HarmonyOS is no longer a &ldquo;should we consider it&rdquo; question, it&rsquo;s a &ldquo;when do we have to&rdquo; question.</strong> Especially in government and enterprise, finance, automotive and IoT, where it&rsquo;s moving faster than many expected.</p>
<p>And the choice KMP teams faced with HarmonyOS used to be awkward:</p>
<ul>
<li><strong>Shared logic carries over, but the UI gets rewritten</strong> (ArkUI) → which means an extra person on the HarmonyOS side</li>
<li>Or <strong>drop KMP entirely</strong> and start a separate HarmonyOS project → all the shared-logic advantage gone</li>
</ul>
<p>The point of this release is right there: <strong>it takes most of the &ldquo;HarmonyOS UI has to be rewritten too&rdquo; cost out of the equation.</strong></p>
<p>For a small team, that may be the difference between &ldquo;can&rsquo;t afford it&rdquo; and &ldquo;can afford it&rdquo;.</p>
<h2 id="a-cooler-read-how-to-see-this">A cooler read: how to see this</h2>
<p>Hype is hype. I need to be clear about a few boundaries so you don&rsquo;t misjudge.</p>
<p><strong>First, this is a community SIG project, not JetBrains official.</strong></p>
<p>The publisher is the <strong>CPF-KMP-CMP SIG</strong>, not JetBrains and not an official Huawei release. That doesn&rsquo;t make it bad — quite the opposite, this kind of &ldquo;ecosystem foundation&rdquo; usually gets chewed through by community SIGs bit by bit — but <strong>set your expectations accordingly</strong>:</p>
<ul>
<li>Long-term maintenance depends on community activity, without the explicit commitments an official API carries</li>
<li>When you hit a problem, the answer is in the community (issues, discussions), not in official docs</li>
</ul>
<p><strong>Second, unified rendering needs API 19+.</strong></p>
<p>If your target devices include a lot of older HarmonyOS builds, you don&rsquo;t get that biggest performance dividend, so assess your device distribution.</p>
<p><strong>Third, it solves a real problem.</strong></p>
<p>Cross-platform on HarmonyOS used to offer basically two options:</p>
<ul>
<li><strong>Write ArkUI separately for HarmonyOS</strong>, maintaining two UIs</li>
<li><strong>Skip HarmonyOS</strong>, wait for official support</li>
</ul>
<p>Now there&rsquo;s a third: <strong>keep writing shared logic, reuse the Compose UI, bridge existing ArkTS with akinterop.</strong></p>
<p>For a team that&rsquo;s already invested in KMP, the cost-benefit of that third road is hard to ignore.</p>
<h2 id="put-it-back-in-the-arc-of-the-last-few-days">Put it back in the arc of the last few days</h2>
<p>Funny thing: this lines up with two topics from the last couple of days into one thread.</p>
<p>In <strong>the Jake Wharton piece</strong>, he puts it clearly:</p>
<blockquote>
<p>Knowing Kotlin is no longer a career advantage&hellip; but having something more advanced like Kotlin Multiplatform can still be a differentiator.</p>
</blockquote>
<p><strong>The Shopify piece</strong> concludes: once AI makes &ldquo;reimplementing it&rdquo; cheap, the &ldquo;share the UI&rdquo; camp takes the biggest hit, while &ldquo;shared logic + native UI&rdquo; holds up better.</p>
<p>And then today: <strong>KMP&rsquo;s map just gained another region — and a big one.</strong></p>
<p>Three things point the same way: <strong>the value of cross-platform is shifting from &ldquo;save headcount&rdquo; to &ldquo;architecture governance + ecosystem coverage&rdquo;.</strong></p>
<p>Chewing through platforms one at a time was always the core story of the KMP bet. With the HarmonyOS piece filled in, for developers in China especially — <strong>it may be the first time many of them have a real reason to look at KMP seriously.</strong></p>
<hr>
<p>One question to leave you with:</p>
<p><strong>If you can write HarmonyOS UI in Compose, would you still maintain a separate ArkUI codebase for it, or just extend your KMP project to HarmonyOS?</strong></p>
<p>And — for the project in your hands, is HarmonyOS a &ldquo;must do&rdquo; or a &ldquo;let&rsquo;s see&rdquo;?</p>
<p>Comments are open, I really want to know where people actually are.</p>
<hr>
<p><em>References:</em></p>
<ul>
<li><em><a href="https://news.mydrivers.com/1/1151/1151178.htm">First official HarmonyOS release of KMP&amp;CMP (Kuai Technology / Drive Home, 2026-09-15)</a></em></li>
<li><em>Docs: search <code>CPF-KMP-CMP/docs</code> on AtomGit</em></li>
</ul>
]]></content:encoded></item></channel></rss>