<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0" xmlns:cc="http://cyber.law.harvard.edu/rss/creativeCommonsRssModule.html">
    <channel>
        <title><![CDATA[Huawei Developers - Medium]]></title>
        <description><![CDATA[As Huawei Developers, our Medium publication where we share information about the use of Huawei Ecosystem - Medium]]></description>
        <link>https://medium.com/huawei-developers?source=rss----25443cf6c73---4</link>
        <image>
            <url>https://cdn-images-1.medium.com/proxy/1*TGH72Nnw24QL3iV9IOm4VA.png</url>
            <title>Huawei Developers - Medium</title>
            <link>https://medium.com/huawei-developers?source=rss----25443cf6c73---4</link>
        </image>
        <generator>Medium</generator>
        <lastBuildDate>Sat, 16 May 2026 18:12:01 GMT</lastBuildDate>
        <atom:link href="https://medium.com/feed/huawei-developers" rel="self" type="application/rss+xml"/>
        <webMaster><![CDATA[yourfriends@medium.com]]></webMaster>
        <atom:link href="http://medium.superfeedr.com" rel="hub"/>
        <item>
            <title><![CDATA[How to use AudioCapturer and AudioRenderer with ArkTS?]]></title>
            <link>https://medium.com/huawei-developers/how-to-use-audiocapturer-and-audiorenderer-with-arkts-488823663dbf?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/488823663dbf</guid>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[wearables]]></category>
            <category><![CDATA[audio]]></category>
            <category><![CDATA[ark-ts]]></category>
            <dc:creator><![CDATA[Mehmet Karaaslan]]></dc:creator>
            <pubDate>Wed, 25 Mar 2026 10:52:57 GMT</pubDate>
            <atom:updated>2026-04-03T06:13:08.745Z</atom:updated>
            <content:encoded><![CDATA[<h4>Let’s build with ArkTS</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*Oi14tmF2gydQfNZHXBsxUQ.png" /><figcaption>How to use AudioCapturer and AudioRenderer with ArkTS?</figcaption></figure><h3>Introduction</h3><p>Have you ever felt that traditional audio recording methods weren’t enough for you? Have you ever wanted to explore more?</p><p>Welcome to explore AudioCapturer.</p><blockquote>The AudioCapturer is used to record Pulse Code Modulation (PCM) audio data. It is suitable if you have extensive audio development experience and want to implement more flexible recording features.</blockquote><blockquote><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/using-audiocapturer-for-recording">AudioCapturer</a></blockquote><h4>AudioCapturer</h4><p>To make things easier for ourselves, we will create an @Observed class.</p><p>Create Recorder.ets under ../ets/service directory.</p><pre>import { audio } from &#39;@kit.AudioKit&#39;;<br><br>@Observed<br>export default class Recorder {<br>  private capturer?: audio.AudioCapturer;<br>  state: string = &#39;STATE_INVALID&#39;;<br><br>  get recording(): boolean {<br>    return this.state === &#39;STATE_RUNNING&#39;;<br>  }<br>}</pre><p>Time to record. Let’s start with defining audio parameters. Create Constants.ets under the service directory.</p><pre>import { audio } from &#39;@kit.AudioKit&#39;;<br><br>export const streamInfo: audio.AudioStreamInfo = {<br>  samplingRate: audio.AudioSamplingRate.SAMPLE_RATE_48000,<br>  channels: audio.AudioChannel.CHANNEL_2,<br>  sampleFormat: audio.AudioSampleFormat.SAMPLE_FORMAT_S16LE,<br>  encodingType: audio.AudioEncodingType.ENCODING_TYPE_RAW,<br>  channelLayout: audio.AudioChannelLayout.CH_LAYOUT_STEREO<br>};</pre><p>You can play with these as you wish. We defined this as a separate parameter because we will use it for AudioCapturer and AudioRenderer.</p><p>Now we can record. We will start by creating an AudioCapturer object.</p><pre>async start(filePath: string) {<br>  try {<br>    this.capturer = await audio.createAudioCapturer({<br>        capturerInfo: {<br>          source: audio.SourceType.SOURCE_TYPE_MIC,<br>          capturerFlags: 0<br>        },<br>        streamInfo: streamInfo<br>      });<br><br>      // rest will come here<br>  } catch (e) {<br>    console.error(&#39;start err:&#39;, e.code, e.message);<br>  }<br>}</pre><p>After we have a capturer, we can attach listeners. Start with on(&#39;stateChange&#39;). This will inform when the capturer’s state changes.</p><pre>this.capturer.on(&#39;stateChange&#39;, (state: audio.AudioState) =&gt; {<br>  switch (state) {<br>    case audio.AudioState.STATE_INVALID:<br>      this.state = &#39;STATE_INVALID&#39;;<br>      break;<br>    case audio.AudioState.STATE_NEW:<br>      this.state = &#39;STATE_NEW&#39;;<br>      break;<br>    case audio.AudioState.STATE_PREPARED:<br>      this.state = &#39;STATE_PREPARED&#39;;<br>      break;<br>    case audio.AudioState.STATE_RUNNING:<br>      this.state = &#39;STATE_RUNNING&#39;;<br>      break;<br>    case audio.AudioState.STATE_STOPPED:<br>      this.state = &#39;STATE_STOPPED&#39;;<br>      break;<br>    case audio.AudioState.STATE_RELEASED:<br>      this.state = &#39;STATE_RELEASED&#39;;<br>      break;<br>    case audio.AudioState.STATE_PAUSED:<br>      this.state = &#39;STATE_PAUSED&#39;;<br>      break;<br>  }<br><br>  console.info(`audioCapturer state changed to: ${this.state}`);<br>});</pre><p>Second, we can listen for audio interruptions and act accordingly. Here we will cover some cases; you can always add more.</p><pre>this.capturer.on(&#39;audioInterrupt&#39;, (interruptEvent: audio.InterruptEvent) =&gt; {<br>  if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {<br>    switch (interruptEvent.hintType) {<br>      case audio.InterruptHint.INTERRUPT_HINT_PAUSE:<br>      case audio.InterruptHint.INTERRUPT_HINT_STOP:<br>        this.stopAndRelease();<br>        break;<br>      default:<br>        console.info(&#39;Invalid interruptEvent&#39;);<br>        break;<br>    }<br>  }<br>});</pre><p>Finally, we must attach on(&#39;readData&#39;). This will be triggered periodically when the system must read an audio stream. Basically, when the capturer gets data from the microphone.</p><p>We will use this to save data to a file.</p><ul><li>First, define a parameter to store the file fd. And another one to track the file write location. We do not want to override existing data, right?</li></ul><pre>private fileFd?: number;<br>private fileWriteOffset: number = 0;</pre><ul><li>Create/open file, store file fd.</li></ul><pre>const audioFile: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE);<br>this.fileFd = audioFile.fd;<br>console.info(`file fd: ${this.fileFd}`);<br><br>this.fileWriteOffset = 0;</pre><ul><li>Attach the listener. This will write incoming data into the file.</li></ul><pre>this.capturer?.on(&#39;readData&#39;, (buffer) =&gt; {<br>  console.info(`read | len: ${buffer.byteLength} | offset: ${this.fileWriteOffset} | fd: ${this.fileFd}`);<br>  fs.writeSync(this.fileFd, buffer, {<br>    offset: this.fileWriteOffset,<br>    length: buffer.byteLength<br>  });<br><br>  this.fileWriteOffset += buffer.byteLength;<br>});</pre><p>And now, we can actually start the recorder.</p><pre>await this.capturer.start();</pre><h4>AudioRenderer</h4><p>To play recorded audio clips, we will use AudioRenderer. Create Player under service directory.</p><p>We will follow the same logic with the AudioCapturer. Create the AudioRenderer object, attach listeners, read the file, and play.</p><pre>import { audio } from &#39;@kit.AudioKit&#39;;<br>import { fileIo as fs } from &#39;@kit.CoreFileKit&#39;;<br>import { streamInfo } from &#39;./Constants&#39;;<br><br>@Observed<br>export default class Player {<br>  private player?: audio.AudioRenderer;<br>  state: string = &#39;idle&#39;;<br>  private fileFd?: number;<br>  private fileReadOffset: number = 0;<br><br>  private async init(filePath: string) {<br>    try {<br>      this.player = await audio.createAudioRenderer({<br>        rendererInfo: {<br>          usage: audio.StreamUsage.STREAM_USAGE_MUSIC,<br>          rendererFlags: 0<br>        },<br>        streamInfo: streamInfo<br>      });<br><br>      this.player.on(&#39;outputDeviceChangeWithInfo&#39;, (deviceChangeInfo: audio.AudioStreamDeviceChangeInfo) =&gt; {<br>        if (this.state === &#39;STATE_RUNNING&#39;) {<br>          this.pause();<br>        }<br>      });<br><br>      this.player.on(&#39;audioInterrupt&#39;, (interruptEvent: audio.InterruptEvent) =&gt; {<br>        if (interruptEvent.forceType === audio.InterruptForceType.INTERRUPT_FORCE) {<br>          switch (interruptEvent.hintType) {<br>            case audio.InterruptHint.INTERRUPT_HINT_PAUSE:<br>              console.info(&#39;Force paused. Update playing status and stop writing&#39;);<br>              this.pause();<br>              break;<br>            case audio.InterruptHint.INTERRUPT_HINT_STOP:<br>              console.info(&#39;Force stopped. Update playing status and stop writing&#39;);<br>              this.stop();<br>              break;<br>            default:<br>              break;<br>          }<br>        }<br>      });<br><br>      this.player.on(&#39;stateChange&#39;, (state: audio.AudioState) =&gt; {<br>        switch (state) {<br>          case audio.AudioState.STATE_INVALID:<br>            this.state = &#39;STATE_INVALID&#39;;<br>            break;<br>          case audio.AudioState.STATE_NEW:<br>            this.state = &#39;STATE_NEW&#39;;<br>            break;<br>          case audio.AudioState.STATE_PREPARED:<br>            this.state = &#39;STATE_PREPARED&#39;;<br>            break;<br>          case audio.AudioState.STATE_RUNNING:<br>            this.state = &#39;STATE_RUNNING&#39;;<br>            break;<br>          case audio.AudioState.STATE_STOPPED:<br>            this.state = &#39;STATE_STOPPED&#39;;<br>            break;<br>          case audio.AudioState.STATE_RELEASED:<br>            this.state = &#39;STATE_RELEASED&#39;;<br>            break;<br>          case audio.AudioState.STATE_PAUSED:<br>            this.state = &#39;STATE_PAUSED&#39;;<br>            break;<br>        }<br><br>        console.info(`audioRenderer state changed to: ${this.state}`);<br>      });<br><br>      const audioFile: fs.File = fs.openSync(filePath, fs.OpenMode.READ_ONLY);<br>      this.fileFd = audioFile.fd; // Obtain the file FD.<br>      console.info(`file fd: ${this.fileFd}`);<br><br>      this.fileReadOffset = 0;<br><br>      this.player.on(&#39;writeData&#39;, (buffer) =&gt; {<br>        console.info(`writeData | len: ${buffer.byteLength} | offset: ${this.fileReadOffset} | fd: ${this.fileFd}`);<br>        try {<br>          const readLength = fs.readSync(this.fileFd, buffer, {<br>            offset: this.fileReadOffset,<br>            length: buffer.byteLength<br>          });<br><br>          this.fileReadOffset += readLength;<br><br>          if (readLength === 0) {<br>            console.info(&#39;stop now&#39;);<br>            this.player?.off(&#39;writeData&#39;);<br>            this.stop();<br>          }<br><br>          return audio.AudioDataCallbackResult.VALID;<br>        } catch (error) {<br>          console.error(&#39;Error reading file:&#39;, error);<br>          return audio.AudioDataCallbackResult.INVALID;<br>        }<br>      });<br><br>      await this.player.start();<br>    } catch (e) {<br>      console.error(`init err: ${e}`);<br>    }<br>  }<br><br>  async start(filePath: string) {<br>    try {<br>      if (!this.player) {<br>        console.info(&#39;init&#39;);<br>        await this.init(filePath);<br>      } else {<br>        console.info(&#39;start&#39;);<br>        await this.player.start();<br>      }<br>    } catch (e) {<br>      console.error(&#39;start err:&#39;, e);<br>    }<br>  }<br><br>  async pause() {<br>    this.player?.pause();<br>  }<br><br>  async stop() {<br>    try {<br>      await this.player?.stop();<br>      await this.player?.release();<br>      fs.closeSync(this.fileFd);<br>      this.player = undefined;<br>    } catch (e) {<br>      console.error(&#39;stop err:&#39;, e.code, e.message);<br>      throw new Error(e.message);<br>    }<br>  }<br>}</pre><h4>Ready, Set, Action</h4><p>Let’s see things in action. First things first, we need microphone permission.</p><ul><li>Modify module.json5</li></ul><pre>&quot;requestPermissions&quot;: [<br>  {<br>    &quot;name&quot;: &quot;ohos.permission.MICROPHONE&quot;,<br>    &quot;reason&quot;: &quot;$string:mic_reason&quot;,<br>    &quot;usedScene&quot;: {<br>      &quot;when&quot;: &quot;inuse&quot;<br>    }<br>  }<br>]</pre><p>Modify Index.ets</p><pre>import Player from &#39;../service/Player&#39;;<br>import Recorder from &#39;../service/Recorder&#39;;<br>import { abilityAccessCtrl, common, PermissionRequestResult, Permissions } from &#39;@kit.AbilityKit&#39;;<br><br>@Entry<br>@Component<br>struct Index {<br>  private filePath: string = `${this.getUIContext().getHostContext()!.filesDir}/record.pcm`;<br>  @State recorder: Recorder = new Recorder();<br>  @State player: Player = new Player();<br><br>  aboutToAppear(): void {<br>    this.requestMicPermission();<br>  }<br><br>  build() {<br>    Column() {<br>      Text(&#39;Recorder State&#39;);<br>      Text(this.recorder.state);<br><br>      Button(&#39;Record / Stop&#39;)<br>        .size({ height: 40, width: &#39;50%&#39; })<br>        .onClick(() =&gt; {<br>          if (this.recorder.recording) {<br>            this.recorder.stopAndRelease();<br>          } else {<br>            this.recorder.start(this.filePath);<br>          }<br>        });<br><br>      Text(&#39;Player State&#39;);<br>      Text(this.player.state);<br><br>      Button(&#39;Play / Pause&#39;)<br>        .size({ height: 40, width: &#39;50%&#39; })<br>        .onClick(() =&gt; {<br>          if (this.player.state === &#39;STATE_RUNNING&#39;) {<br>            this.player.pause();<br>          } else {<br>            this.player.start(this.filePath);<br>          }<br>        });<br><br>      Button(&#39;Release&#39;)<br>        .size({ height: 40, width: &#39;50%&#39; })<br>        .onClick(() =&gt; {<br>          this.player.stop();<br>        });<br>    }<br>    .justifyContent(FlexAlign.Center)<br>    .height(&#39;100%&#39;)<br>    .width(&#39;100%&#39;);<br>  }<br><br>  async requestMicPermission() {<br>    const atManager: abilityAccessCtrl.AtManager = abilityAccessCtrl.createAtManager();<br>    const context = this.getUIContext().getHostContext();<br>    const permissions: Permissions[] = [&#39;ohos.permission.MICROPHONE&#39;];<br><br>    try {<br>      // request permission from user<br>      let granted =<br>        (await (atManager.requestPermissionsFromUser(context,<br>          permissions) as Promise&lt;PermissionRequestResult&gt;)).authResults[0] === 0;<br>      if (!granted) {<br>        // request permission on settings<br>        granted = (await atManager.requestPermissionOnSetting(context, permissions))[0] ===<br>        abilityAccessCtrl.GrantStatus.PERMISSION_GRANTED;<br><br>        if (!granted) {<br>          await (this.getUIContext().getHostContext() as common.UIAbilityContext).terminateSelf();<br>        }<br>      }<br>    } catch (error) {<br>      const err: BusinessError = error as BusinessError;<br>      console.error(`Failed to check access token. Code is ${err.code}, message is ${err.message}`);<br>    }<br>  }<br>}</pre><p>This will ask for microphone permission when users open the app.</p><h4>Bonus</h4><pre>private filePath: string = `${this.getUIContext().getHostContext()!.filesDir}/record.pcm`;</pre><p>Right now, we are recording audio into the record.pcm file. But you may have realized that if you try to record again, it will override the existing audio.</p><p>Let’s see how we can record audio so that it will not override the existing recording, but continue after that.</p><p>Easy, you only need to change the file open mode inside Recorder.</p><pre>const audioFile: fs.File = fs.openSync(filePath, fs.OpenMode.READ_WRITE | fs.OpenMode.CREATE | fs.OpenMode.APPEND);</pre><h4>Conclusion</h4><p>That’s all, folks. We have seen how to use AudioCapturer and AudioRenderer. Keep up the good work.</p><blockquote><em>You can check the project in GitHub: </em><a href="https://github.com/Explore-In-HMOS-Wearable/how-to-record-audio-over-audio">https://github.com/Explore-In-HMOS-Wearable/how-to-record-audio-over-audio</a></blockquote><p>See you all in new adventures. :)</p><p><strong>~ <em>Fortuna Favet Fortibus</em></strong></p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/using-audiocapturer-for-recording">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/using-audiorenderer-for-playback">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=488823663dbf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/how-to-use-audiocapturer-and-audiorenderer-with-arkts-488823663dbf">How to use AudioCapturer and AudioRenderer with ArkTS?</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing User-Authenticated Access to Secrets with Asset Store Service]]></title>
            <link>https://medium.com/huawei-developers/implementing-user-authenticated-access-to-secrets-with-asset-store-service-1e600f69044f?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/1e600f69044f</guid>
            <category><![CDATA[asset-store]]></category>
            <category><![CDATA[user-authentication]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[harmony-os]]></category>
            <category><![CDATA[harmonyos-next]]></category>
            <dc:creator><![CDATA[Emine İNAN]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 08:22:34 GMT</pubDate>
            <atom:updated>2026-02-26T08:22:33.420Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*l4W0uPckNVgiJquZ" /><figcaption>Photo by <a href="https://unsplash.com/@pechka?utm_source=medium&amp;utm_medium=referral">Dima Pechurin</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>The Asset Store Service (ASSET) provides a robust framework for the secure management of sensitive, short-form data such as application tokens and financial credentials. By leveraging the underlying universal keystore and AES256-GCM encryption within a Trusted Execution Environment (TEE), the service ensures data remains protected even in compromised system states. This article explores securing sensitive data using the Asset Store Kit, implementing TEE-backed encryption and biometric CRUD workflows.</p><h4>Project Requirements</h4><p>The objective is to design a secure storage workflow where access to a secret (e.g., an API token) requires explicit user authentication, such as a PIN, facial recognition, or fingerprint at the time of retrieval. The solution must adhere to the following:</p><ul><li>Store the secret encrypted in the Asset Store Service (Secure Kit).</li><li>Enforce device lock and user authentication before returning plaintext.</li><li>Support the full CRUD lifecycle (save, read, update, remove).</li><li>Provide robust error handling and user-friendly messages.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*DU2ovRPRuaYn6U3gFFkH7Q.png" /><figcaption>Architecture Comparison</figcaption></figure><h4>Core Concepts and Configuration</h4><p>The implementation utilizes the @kit.AssetStoreKit module. Key attributes used to define the security policy include:</p><ul><li><strong>ALIAS</strong>: A unique identifier for the specific secret.</li><li><strong>ACCESSIBILITY</strong>: Sets the device state required for access (e.g., DEVICE_UNLOCKED).</li><li><strong>AUTH_TYPE</strong>: Defines the required verification method (PIN, Biometrics, or both).</li><li><strong>AUTH_VALIDITY_PERIOD</strong>: A grace window (typically 30–120s) where subsequent reads do not require re-authentication.</li></ul><h4>Authenticated Query Workflow</h4><p>To maintain a high security posture, the retrieval process follows a three-step handshake:</p><ol><li><strong>preQuery</strong>: Generates a unique challenge from the Asset Service.</li><li><strong>User Authentication</strong>: Validates the user’s identity via the system’s UserIAM, binding the result to the challenge.</li><li><strong>query &amp; </strong><strong>postQuery</strong>: Finalizes the data retrieval and closes the secure session.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*Ba9hubwIwMMzTry7tT7Lwg.png" /><figcaption>Query Flow</figcaption></figure><h4>Technical Implementation</h4><ol><li>Choose an alias for the secret (e.g., secure_api_token_v1).</li><li>Save the secret with authentication requirements:</li></ol><ul><li>ACCESSIBILITY=DEVICE_UNLOCKED</li><li>REQUIRE_PASSWORD_SET=true</li><li>AUTH_TYPE=ANY</li><li>Optional: AUTH_VALIDITY_PERIOD=30–120s</li></ul><p>3. Read with user authentication:</p><ul><li>Call asset.preQuery() → receive a challenge.</li><li>Perform system authentication (PIN/biometric) bound to that challenge.</li><li>Call asset.query() to fetch the data.</li><li>Call asset.postQuery() with the challenge to close the flow.</li></ul><p>4. Update and Remove follow the same pattern if user auth is required.</p><p>5. Implement robust error handling and map technical codes to clear UX messages.</p><h4><strong>Service Layer: </strong><strong>assetAuthService.ets</strong></h4><p>This module encapsulates the logic for secure storage and authenticated access.</p><pre>import { asset } from &#39;@kit.AssetStoreKit&#39;;<br>import util from &#39;@ohos.util&#39;;<br><br>const enc = new util.TextEncoder();<br>const dec = new util.TextDecoder();<br><br>function toBytes(s: string): Uint8Array {<br>  return enc.encode(s);<br>}<br>function fromBytes(b?: Uint8Array): string {<br>  return b ? dec.decode(b) : &#39;&#39;;<br>}<br><br>export const TOKEN_ALIAS = &#39;secure_api_token_v1&#39;;<br><br>// 1) Save secret with authentication policy<br>export async function saveSecretWithPolicy(secret: string): Promise&lt;void&gt; {<br>  const attrs: asset.AssetMap = new Map();<br>  attrs.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br>  attrs.set(asset.Tag.SECRET, toBytes(secret));<br>  attrs.set(asset.Tag.ACCESSIBILITY, asset.Accessibility.DEVICE_UNLOCKED);<br>  attrs.set(asset.Tag.REQUIRE_PASSWORD_SET, true);<br>  attrs.set(asset.Tag.AUTH_TYPE, asset.AuthType.ANY);<br>  attrs.set(asset.Tag.AUTH_VALIDITY_PERIOD, 60_000); // 60s grace window<br><br>  try {<br>    await asset.add(attrs);<br>  } catch (e) {<br>    const code = (e as { code?: number | string }).code;<br>    if (String(code) === &#39;24000003&#39;) {<br>      const upd: asset.AssetMap = new Map();<br>      upd.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br>      upd.set(asset.Tag.SECRET, toBytes(secret));<br>      await asset.update(upd);<br>      return;<br>    }<br>    throw e;<br>  }<br>}<br><br>// 2) Authenticated read<br>export async function readSecretWithUserAuth(<br>  doUserAuth: (challenge: Uint8Array) =&gt; Promise&lt;Uint8Array&gt;<br>): Promise&lt;string | null&gt; {<br>  const query: asset.AssetMap = new Map();<br>  query.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br><br>  const challenge: Uint8Array = await asset.preQuery(query);<br>  await doUserAuth(challenge);<br><br>  const results: Array&lt;asset.AssetMap&gt; = await asset.query(query);<br><br>  const handle: asset.AssetMap = new Map();<br>  handle.set(asset.Tag.AUTH_CHALLENGE, challenge);<br>  await asset.postQuery(handle);<br><br>  if (!results.length) return null;<br>  const secretBytes = results[0].get(asset.Tag.SECRET) as Uint8Array | undefined;<br>  return fromBytes(secretBytes);<br>}<br><br>// 3) Authenticated update<br>export async function updateSecretWithUserAuth(<br>  newValue: string,<br>  doUserAuth: (challenge: Uint8Array) =&gt; Promise&lt;Uint8Array&gt;<br>): Promise&lt;void&gt; {<br>  const query: asset.AssetMap = new Map();<br>  query.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br><br>  const challenge: Uint8Array = await asset.preQuery(query);<br>  await doUserAuth(challenge);<br><br>  const upd: asset.AssetMap = new Map();<br>  upd.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br>  upd.set(asset.Tag.SECRET, toBytes(newValue));<br>  await asset.update(upd);<br><br>  const handle: asset.AssetMap = new Map();<br>  handle.set(asset.Tag.AUTH_CHALLENGE, challenge);<br>  await asset.postQuery(handle);<br>}<br><br>// 4) Authenticated remove<br>export async function removeSecretWithUserAuth(<br>  doUserAuth: (challenge: Uint8Array) =&gt; Promise&lt;Uint8Array&gt;<br>): Promise&lt;void&gt; {<br>  const query: asset.AssetMap = new Map();<br>  query.set(asset.Tag.ALIAS, toBytes(TOKEN_ALIAS));<br><br>  const challenge: Uint8Array = await asset.preQuery(query);<br>  await doUserAuth(challenge);<br><br>  await asset.remove(query);<br><br>  const handle: asset.AssetMap = new Map();<br>  handle.set(asset.Tag.AUTH_CHALLENGE, challenge);<br>  await asset.postQuery(handle);<br>}<br></pre><h4>AuthPage.ets (Wearable UI with PIN keypad + Success screen)</h4><pre>import router from &#39;@ohos.router&#39;;<br>import { readSecretWithUserAuth } from &#39;../services/assetAuthService&#39;;<br><br>@Entry<br>@Component<br>struct AuthPage {<br>  @State pinInput: string = &#39;&#39;;<br>  @State status: string = &#39;Enter PIN to unlock&#39;;<br><br>  private async onUnlock(): Promise&lt;void&gt; {<br>    try {<br>      const val = await readSecretWithUserAuth(async (challenge) =&gt; {<br>        // Here, PIN entry should be verified against system IAM<br>        // For demo, we just return empty<br>        return new Uint8Array(0);<br>      });<br>      this.status = `Unlocked: ${val ?? &#39;&#39;}`;<br>      router.pushUrl({ url: &#39;pages/AuthSuccessPage&#39;, params: { token: val ?? &#39;&#39; } });<br>    } catch (e) {<br>      this.status = &#39;Auth failed&#39;;<br>    }<br>  }<br><br>  build() {<br>    Column() {<br>      Text(this.status).fontSize(10).margin({ bottom: 6 }).fontColor(&#39;#fff&#39;)<br><br>      // PIN entry display<br>      Row() {<br>        Text(&#39;*&#39;.repeat(this.pinInput.length)).fontSize(14).fontColor(&#39;#4CAF50&#39;)<br>      }.margin({ bottom: 8 })<br><br>      // Keypad grid<br>      GridRow() {<br>        [&#39;1&#39;,&#39;2&#39;,&#39;3&#39;,&#39;4&#39;,&#39;5&#39;,&#39;6&#39;,&#39;7&#39;,&#39;8&#39;,&#39;9&#39;,&#39;Delete&#39;,&#39;0&#39;,&#39;Unlock&#39;].forEach((label) =&gt; {<br>          Button(label)<br>            .height(28).width(48)<br>            .margin({ top: 4, bottom: 4, left: 2, right: 2 })<br>            .fontSize(10)<br>            .onClick(() =&gt; {<br>              if (label === &#39;Delete&#39;) {<br>                this.pinInput = this.pinInput.slice(0, -1);<br>              } else if (label === &#39;Unlock&#39;) {<br>                this.onUnlock();<br>              } else {<br>                this.pinInput += label;<br>              }<br>            })<br>        })<br>      }<br>    }<br>    .width(&#39;100%&#39;).height(&#39;100%&#39;)<br>    .backgroundColor(&#39;#000&#39;)<br>    .alignItems(HorizontalAlign.Center)<br>    .justifyContent(FlexAlign.Center)<br>  }<br>}</pre><h4>AuthSuccessPage.ets (After unlock)</h4><pre>import router from &#39;@ohos.router&#39;;<br><br>@Entry<br>@Component<br>struct AuthSuccessPage {<br>  @State token: string = &#39;&#39;;<br><br>  build() {<br>    Column() {<br>      Image($r(&#39;app.media.ic_success&#39;)).width(36).height(36).margin({ bottom: 8 })<br><br>      Text(&#39;Access Granted&#39;)<br>        .fontSize(12)<br>        .fontColor(&#39;#4CAF50&#39;)<br>        .fontWeight(FontWeight.Bold)<br>        .margin({ bottom: 6 })<br><br>      Text(`Token: ${this.token}`)<br>        .fontSize(9)<br>        .fontColor(&#39;#FFFFFF&#39;)<br>        .margin({ bottom: 10 })<br><br>      Button(&#39;Done&#39;)<br>        .width(&#39;60%&#39;).height(24).fontSize(9)<br>        .borderRadius(10).backgroundColor(&#39;#4CAF50&#39;)<br>        .onClick(() =&gt; router.back())<br>    }<br>    .justifyContent(FlexAlign.Center)<br>    .alignItems(HorizontalAlign.Center)<br>    .width(&#39;100%&#39;).height(&#39;100%&#39;)<br>    .backgroundColor(&#39;#000&#39;)<br>  }<br>}</pre><h4>Test Results</h4><ul><li><strong>No lock screen set:</strong> Reads fail until user sets PIN/password.</li><li><strong>With lock screen:</strong> Read prompts for user auth.</li><li><strong>Grace period:</strong> Reads within validity window skip extra prompts.</li><li><strong>Update/Remove:</strong> Verified with same preQuery → auth → operation → postQuery pattern.</li><li><strong>Data integrity:</strong> Returned secret matches last saved; remove clears it.</li></ul><figure><img alt="" src="https://cdn-images-1.medium.com/max/755/1*eVU8P12pQ-1x3ayMsNWheA.png" /><figcaption>Output</figcaption></figure><h3>Conclusion</h3><p>By integrating the Asset Store Kit with mandatory user authentication, developers can protect critical application data against both digital attacks and unauthorized physical access. This layered security approach combining TEE encryption with real-time biometric verification is essential for modern applications handling high-value secrets.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/asset-store-kit/">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/asset-store-kit/">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=1e600f69044f" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/implementing-user-authenticated-access-to-secrets-with-asset-store-service-1e600f69044f">Implementing User-Authenticated Access to Secrets with Asset Store Service</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Implement HCE Payment in LiteWearables?]]></title>
            <link>https://medium.com/huawei-developers/how-to-implement-hce-payment-in-litewearables-76426e791967?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/76426e791967</guid>
            <category><![CDATA[che]]></category>
            <category><![CDATA[wearables]]></category>
            <category><![CDATA[lite-wearable]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[nfc]]></category>
            <dc:creator><![CDATA[Mehmet Karaaslan]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 07:27:11 GMT</pubDate>
            <atom:updated>2026-02-26T07:27:09.626Z</atom:updated>
            <content:encoded><![CDATA[<h4>Let’s build with ArkUI.Lite</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*NrnEoJ3DQ4nJcNbLA8o4eA.png" /><figcaption>HCE Payment</figcaption></figure><h3>Introduction</h3><p>Hi fellow devs.</p><p>Today, we will once more learn how to use our smart watches to emulate cards and make payments.</p><blockquote>This article will cover HCE Payment for Lite Wearables. For ArkTS implementation, please check: <a href="https://medium.com/huawei-developers/how-to-implement-hce-payment-in-smart-wearables-60481c64c439">How to Implement HCE Payment in Smart Wearables?</a></blockquote><h4>Configurations</h4><p>Everything goes with an order, so do we. Modify config.json.</p><ul><li>Add permission:</li></ul><pre>&quot;reqPermissions&quot;: [<br>  {<br>    &quot;name&quot;: &quot;ohos.permission.NFC_CARD_EMULATION&quot;,<br>    &quot;reason&quot;: &quot;NFC is used for payment.&quot;,<br>    &quot;usedScene&quot;: {<br>      &quot;when&quot;: &quot;always&quot;<br>    }<br>  }<br>]</pre><ul><li>Add card emulation action</li></ul><pre>&quot;skills&quot;: [<br>  {<br>    &quot;entities&quot;: [<br>      &quot;ohos.nfc.cardemulation.action.HOST_APDU_SERVICE&quot;<br>    ],<br>    &quot;actions&quot;: [<br>      &quot;ohos.nfc.cardemulation.action.HOST_APDU_SERVICE&quot;<br>    ]<br>  }<br>],</pre><ul><li>Add paymentAid</li></ul><pre>&quot;metaData&quot;: {<br>  &quot;customizeData&quot;: [<br>    {<br>      &quot;name&quot;: &quot;paymentAid&quot;,<br>      &quot;value&quot;: &quot;325041592E5359532E4444463031&quot; // Visa International<br>    },<br>    {<br>      &quot;name&quot;: &quot;paymentAid&quot;,<br>      &quot;value&quot;: &quot;A0000000031010&quot; // Visa credit or debit<br>    },<br>    {<br>      &quot;name&quot;: &quot;paymentAid&quot;,<br>      &quot;value&quot;: &quot;A0000000041010&quot; // Mastercard credit or debit<br>    }<br>  ]<br>}</pre><blockquote>An <em>application identifier</em> (AID) is used to address an application in the card or Host Card Emulation (HCE) if delivered without a card.</blockquote><blockquote><a href="https://en.wikipedia.org/wiki/EMV">https://en.wikipedia.org/wiki/EMV</a></blockquote><h4>File Storage</h4><pre>import file from &#39;@system.file&#39;;</pre><p>As you know, Lite wearables do not have a relational database system. Thus, we will use file storage to store the Card and SUKs. We will not dive into details here. You can check the project for the implementation.</p><h4>HCE</h4><p>Let’s spend some money.</p><p>If you have read the ArkTS version, you know that the app can process payments in the foreground and background. Here, things are a bit different. We do not have a background mode. If the app is not open, the OS will open the app to process the payment.</p><pre>import { deleteSuk, getCard, getNextSuk, hasCard, numberOfSuks } from &#39;./dataStore&#39;;<br>import cardEmulation from &#39;@ohos.nfc.cardEmulation&#39;;<br>import router from &#39;../common/js/router&#39;;<br><br>// TODO: this must match with config.json<br>const paymentAid = [<br>    &#39;325041592E5359532E4444463031&#39;,<br>    &#39;A0000000031010&#39;,<br>    &#39;A0000000041010&#39;<br>];<br><br>let card;<br>let sukIndex;<br>let sukData;<br><br>function initialize() {<br>    card = getCard();<br>    let { index, suk } = getNextSuk();<br>    sukIndex = index;<br>    sukData = suk;<br>    console.info(`initialize | ${JSON.stringify(card)} | ${sukIndex} | ${JSON.stringify(sukData)}`);<br>}<br><br>export function deleteHce() {<br>    card = sukIndex = sukData = null;<br>}<br><br>export function hce() {<br>    if (!card &amp;&amp; hasCard()) {<br>        console.info(&#39;initialize&#39;);<br>        initialize();<br>    }<br><br>    let hce = new cardEmulation.HceService();<br>    hce.stop(&#39;com.hmosdemos.hcepaymentlite&#39;);<br>    hce.start(&#39;com.hmosdemos.hcepaymentlite&#39;, paymentAid);<br><br>    hce.on(&#39;hceCmd&#39;, async (data, err) =&gt; {<br>        console.info(`hceCmd: ${data}`);<br><br>        if (err) {<br>            console.error(err);<br>            throw new Error(&#39;Error!&#39;);<br>        }<br><br>        if (card &amp;&amp; sukData) {<br>            console.info(&#39;make payment&#39;);<br>            // TODO: make payment<br>            // Basically payment is just a communication with the POS machine.<br>            // You just need to know what to send.<br>            // GOOD LUCK :)<br><br>            // send a dummy response<br>            const response = [0x47, 0x4F, 0x4F, 0x44, 0x20, 0x4C, 0x55, 0x43, 0x4B, 0x20, 0x3A, 0x29, 0x90, 0x00];<br>            hce.transmit(response, () =&gt; {<br>            });<br><br>            // simulate success<br>            // delete last suk<br>            deleteSuk(sukIndex);<br>            deleteHce();<br>            console.info(`payment success remaining suk: ${numberOfSuks()}`);<br><br>            // route to success page<br>            router.go(&#39;pages/cardTransferSuccess/cardTransferSuccess&#39;, { sukCount: numberOfSuks() });<br>        } else if (card) {<br>            console.info(&#39;route to no suk&#39;);<br>            // route to no suk page<br>            router.go(&#39;pages/noSuk/noSuk&#39;);<br>        } else {<br>            console.info(`no card | no suk | ${JSON.stringify(card)} | ${JSON.stringify(sukData)}`);<br>        }<br>    });<br>}</pre><p>The process is as follows:</p><ul><li>When users open the app, the system will initialize the listener.</li><li>Card and SUK are read from file storage.</li></ul><pre>card = getCard();<br>let { index, suk } = getNextSuk();<br>sukIndex = index;<br>sukData = suk;</pre><ul><li>When the app gets data from a POS machine, it will process and send back an answer.</li></ul><pre>hce.transmit(response, () =&gt; {<br>});</pre><p>Basically, payment is just a communication with the POS machine. You just need to know what to send.</p><p>To activate the HCE, we call this in index.js</p><pre>onInit() {<br>    // ...<br>    hce();<br>},</pre><h4>Data</h4><p>In normal cases, you might get card data from an API or from a mobile app. Here we will use mock data to simulate the process.</p><p>Within index.js we can define add and remove functions.</p><pre>addCard() {<br>    addCard({<br>        name: &#39;My Lucky Card&#39;<br>    });<br><br>    this.setCardName();<br><br>    addSuks([{}, {}, {}, {}, {}]);<br><br>    hce();<br>},<br><br>deleteCard() {<br>    deleteCard();<br>    this.setCardName();<br>    deleteHce();<br>}</pre><h4>UI</h4><p>Let’s see things in action. On the index page, we will add the current card and the add/remove buttons.</p><pre>&lt;div class=&quot;container&quot;&gt;<br>    &lt;div id=&quot;topDiv&quot;&gt;<br>        &lt;div id=&quot;empty&quot;&gt;&lt;/div&gt;<br>        &lt;div id=&quot;textDiv&quot;&gt;<br>            &lt;text style=&quot;font-size: 24px;&quot;&gt;Contactless Payment&lt;/text&gt;<br>            &lt;text style=&quot;font-size: 20px;&quot;&gt;{{ cardName }}&lt;/text&gt;<br>        &lt;/div&gt;<br><br>        &lt;div id=&quot;buttonDiv&quot;&gt;<br>            &lt;div id=&quot;addButton&quot; @click=&quot;addCard&quot;&gt;<br>                &lt;text style=&quot;font-size: 24px;&quot;&gt;Add Card&lt;/text&gt;<br>            &lt;/div&gt;<br>            &lt;div id=&quot;removeButton&quot; @click=&quot;deleteCard&quot;&gt;<br>                &lt;text style=&quot;font-size: 24px;&quot;&gt;Remove&lt;/text&gt;<br>            &lt;/div&gt;<br>        &lt;/div&gt;<br>    &lt;/div&gt;<br>&lt;/div&gt;</pre><h4>Result</h4><p>It is good, ain’t it? You can check the project for the rest.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*KE_oesYJNCmdPjkkFwXfkA.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*mIV1GwofIJOvsy39aJw-Ug.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*4hv9zQtgHcbWrH7WXJzZaw.png" /><figcaption>It is good, ain’t it?</figcaption></figure><h3>Conclusion</h3><p>That’s all for this article. We have covered host card emulation in Lite wearables.</p><blockquote>You can check the project in GitHub: <a href="https://github.com/Explore-In-HMOS-Wearable/sportwatch-how-to-use-hce">https://github.com/Explore-In-HMOS-Wearable/sportwatch-how-to-use-hce</a></blockquote><p>See you all in new adventures. :)</p><p><strong>~ <em>Fortuna Favet Fortibus</em></strong></p><h3>References</h3><ul><li><a href="https://github.com/Explore-In-HMOS-Wearable/sportwatch-how-to-use-hce">GitHub - Explore-In-HMOS-Wearable/sportwatch-how-to-use-hce</a></li><li><a href="https://en.wikipedia.org/wiki/EMV">EMV - Wikipedia</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-references/js-apis-cardemulation">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=76426e791967" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/how-to-implement-hce-payment-in-litewearables-76426e791967">How to Implement HCE Payment in LiteWearables?</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Implement HCE Payment in Smart Wearables?]]></title>
            <link>https://medium.com/huawei-developers/how-to-implement-hce-payment-in-smart-wearables-60481c64c439?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/60481c64c439</guid>
            <category><![CDATA[nfc]]></category>
            <category><![CDATA[ark-ts]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[wearables]]></category>
            <category><![CDATA[che]]></category>
            <dc:creator><![CDATA[Mehmet Karaaslan]]></dc:creator>
            <pubDate>Thu, 26 Feb 2026 07:20:16 GMT</pubDate>
            <atom:updated>2026-02-26T07:20:15.376Z</atom:updated>
            <content:encoded><![CDATA[<h4>Let’s build with ArkTS</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/800/1*PyVpgs9Jkj-7Th9kQi9WEA.png" /><figcaption>HCE Payment</figcaption></figure><h3>Introduction</h3><p>Hello everybody.</p><p>Today, we will learn how to use our smart watches to emulate cards and make payments. Let’s roll.</p><p>But first, allow me to give you brief domain knowledge:</p><blockquote>EMV is a payment method based on a technical standard developed by Europay, Mastercard, and Visa.</blockquote><blockquote>Developed for payment cards, Point Of Sale (POS) terminals, and automated teller machines (ATM)</blockquote><blockquote>An application identifier (AID) is used to address an application in the card or Host Card Emulation (HCE) device.</blockquote><blockquote>It is used to identify the type of product, so that all product issuers (Visa, Mastercard, etc.) must have their own application.</blockquote><blockquote>EMV uses predefined protocols for data transmission, and data is exchanged in application protocol data units (APDUs)</blockquote><blockquote>For more information, check: <a href="https://en.wikipedia.org/wiki/EMV">https://en.wikipedia.org/wiki/EMV</a></blockquote><h4>Configurations</h4><p>Let’s start with configurations. Modify module.json5.</p><ul><li>Add permission</li></ul><pre>&quot;requestPermissions&quot;: [<br>  {<br>    &quot;name&quot;: &quot;ohos.permission.NFC_CARD_EMULATION&quot;,<br>    &quot;reason&quot;: &quot;$string:NFC_reason&quot;<br>  }<br>]</pre><ul><li>Add card emulation action</li></ul><pre>&quot;skills&quot;: [<br>  {<br>    &quot;entities&quot;: [<br>      &quot;entity.system.home&quot;<br>    ],<br>    &quot;actions&quot;: [<br>      &quot;ohos.want.action.home&quot;,<br>      &quot;ohos.nfc.cardemulation.action.HOST_APDU_SERVICE&quot;<br>    ]<br>  }<br>],</pre><ul><li>Add payment-aid</li></ul><pre>&quot;metadata&quot;: [<br>  {<br>    &quot;name&quot;: &quot;payment-aid&quot;,<br>    &quot;value&quot;: &quot;325041592E5359532E4444463031&quot; // Visa International<br>  {<br>    &quot;name&quot;: &quot;payment-aid&quot;,<br>    &quot;value&quot;: &quot;A0000000031010&quot; // Visa credit or debit<br>  },<br>  {<br>    &quot;name&quot;: &quot;payment-aid&quot;,<br>    &quot;value&quot;: &quot;A0000000041010&quot; // Mastercard credit or debit<br>  }<br>]</pre><blockquote>An <em>application identifier</em> (AID) is used to address an application in the card or Host Card Emulation (HCE) if delivered without a card.</blockquote><blockquote><a href="https://en.wikipedia.org/wiki/EMV">https://en.wikipedia.org/wiki/EMV</a></blockquote><h4>RDB</h4><p>We will use the relational database to store the Card and Suks. We will not go into details of the RDB. You can check the project for the implementation.</p><p>Let’s define our data models. We will create these under the model directory.</p><ul><li>Card.ets</li></ul><pre>export interface Card {<br>  name: string;<br>  // TODO: define rest of the parameters if needed<br>}</pre><ul><li>SUK.ets</li></ul><pre>export interface SUK {<br>  // TODO: define necessary parameters<br>}<br><br>export interface DbSUK {<br>  id: number;<br>  suk: SUK;<br>}</pre><p>In our app, there will be only one card and multiple SUKs. I can see you smiling and trying to understand why we have the DbSUK interface. In real-life scenarios, using the power of JS, you can convert a given JSON data to an object. So we use DbSUK to benefit from this power and store SUK data without defining all the parameters. Also, we need a PK for our database.</p><h4>HceHandler</h4><p>Here is the main deal. Let’s see what it is.</p><pre>import { cardEmulation } from &#39;@kit.ConnectivityKit&#39;;</pre><p>This is the main character. As its name gives away, we use this kit to make our app emulate a bank card.</p><pre>type rt = &#39;foreground&#39; | &#39;background&#39;;</pre><p>We will keep track of whether our app runs in the foreground or background. This is not necessary in many cases, but in our case, we will route the app to the success page after the card read operation is successfull and as you might guess route operation causes the app to fail if it is called in the background.</p><pre>await HceHandler.hceService!.transmit(response);</pre><p>This is how we send a response to the POS machine. Basically, payment is just a communication with the POS machine. You just need to know what to send.</p><pre>import { cardEmulation } from &#39;@kit.ConnectivityKit&#39;;<br>import { bundleManager } from &#39;@kit.AbilityKit&#39;;<br>import { Rdb } from &#39;../rdb/Rdb&#39;;<br>import { CardTable } from &#39;../rdb/tables/CardTable&#39;;<br>import { SUKTable } from &#39;../rdb/tables/SukTable&#39;;<br><br>type rt = &#39;foreground&#39; | &#39;background&#39;;<br><br>export default class HceHandler {<br>  private constructor() {<br>  }<br><br>  private static hceService: cardEmulation.HceService | undefined | null = undefined;<br>  private static runType: rt = &#39;background&#39;;<br><br>  static runInBackground() {<br>    HceHandler.runType = &#39;background&#39;;<br>  }<br><br>  static enable(context: Context, _runType: rt, pageStack?: NavPathStack) {<br>    console.info(&#39;start enable&#39;);<br>    HceHandler.runType = _runType;<br>    // first check availability<br>    if (canIUse(&#39;SystemCapability.Communication.NFC.Core&#39;) &amp;&amp; cardEmulation.hasHceCapability()) {<br>      try {<br>        const hceElementName: bundleManager.ElementName = {<br>          bundleName: &#39;com.hmosdemos.hcepayment&#39;, // TODO: this must match with the app&#39;s bundle<br>          abilityName: &#39;EntryAbility&#39;,<br>          moduleName: &#39;entry&#39;<br>        };<br><br>        // TODO: this must match with module.json5<br>        const paymentAid = [<br>          &#39;325041592E5359532E4444463031&#39;,<br>          &#39;A0000000031010&#39;,<br>          &#39;A0000000041010&#39;<br>        ];<br><br>        HceHandler.hceService?.stop(hceElementName);<br>        HceHandler.hceService = new cardEmulation.HceService();<br>        HceHandler.hceService.start(hceElementName, paymentAid);<br><br>        HceHandler.hceService.on(&#39;hceCmd&#39;, async (err, data) =&gt; {<br>          console.info(`hceCmd ${data}`);<br>          if (err) {<br>            console.error(`HceHandler callback Error: ${err}`);<br>            return;<br>          }<br><br>          // get card data from db<br>          await Rdb.instance.init(context);<br>          const card = CardTable.get();<br>          const suk = SUKTable.getLast();<br><br>          if (card &amp;&amp; suk) {<br>            // TODO: make payment<br>            // Basically payment is just a communication with the POS machine.<br>            // You just need to know what to send.<br>            // GOOD LUCK :)<br><br>            // send a dummy response<br>            const response = [0x47, 0x4F, 0x4F, 0x44, 0x20, 0x4C, 0x55, 0x43, 0x4B, 0x20, 0x3A, 0x29, 0x90, 0x00];<br>            await HceHandler.hceService!.transmit(response);<br><br>            // simulate success<br>            // delete last suk<br>            SUKTable.delete(suk);<br>            console.info(`payment success remaining suk: ${SUKTable.getCount()}`);<br><br>            // route to success page<br>            if (HceHandler.runType === &#39;foreground&#39; &amp;&amp; pageStack) {<br>              const lastPage = pageStack.getAllPathName().pop();<br>              if (lastPage === &#39;CardTransferSuccess&#39; || lastPage === &#39;NoSuk&#39;) {<br>                pageStack.replacePath({ name: &#39;CardTransferSuccess&#39; });<br>              } else {<br>                pageStack.pushPath({ name: &#39;CardTransferSuccess&#39; });<br>              }<br>            }<br><br>          } else if (card &amp;&amp; SUKTable.getCount() === 0) {<br>            // route to no suk page<br>            if (HceHandler.runType === &#39;foreground&#39; &amp;&amp; pageStack) {<br>              const lastPage = pageStack.getAllPathName().pop();<br>              if (lastPage === &#39;CardTransferSuccess&#39;) {<br>                pageStack.replacePath({ name: &#39;NoSuk&#39; });<br>              } else if (lastPage !== &#39;NoSuk&#39;) {<br>                pageStack.pushPath({ name: &#39;NoSuk&#39; });<br>              }<br>            }<br>          }<br>        });<br>      } catch (error) {<br>        console.error(`HceHandler errCode: ${error.code} errMessage: ${error.message}`);<br>      }<br>    }<br>  }<br>};</pre><h4>User Interface</h4><ul><li>Success Page — CardReadSuccess.ets</li></ul><pre>import { SUKTable } from &#39;../rdb/tables/SukTable&#39;;<br><br>@Component<br>export default struct CardTransferSuccess {<br>  @Consume(&#39;mainStack&#39;) pageStack: NavPathStack;<br>  sukCount: number = SUKTable.getCount();<br><br>  build() {<br>    NavDestination() {<br>      Column() {<br>        Column() {<br>          SymbolGlyph($r(&#39;sys.symbol.checkmark_circle_fill&#39;))<br>            .fontColor([Color.Green])<br>            .fontSize(24)<br>            .margin({ bottom: 8 });<br>          Text(`Card Transfer Success, Remaining: ${this.sukCount}`);<br>        }<br>        .size({ width: &#39;70%&#39;, height: &#39;70%&#39; })<br>        .alignItems(HorizontalAlign.Center)<br>        .justifyContent(FlexAlign.Center);<br>      }<br>      .size({ width: &#39;100%&#39;, height: &#39;100%&#39; })<br>      .backgroundColor(Color.Black)<br>      .alignItems(HorizontalAlign.Center)<br>      .justifyContent(FlexAlign.Center);<br>    }<br>    .hideTitleBar(true);<br>  }<br>}</pre><ul><li>NoSuk.ets</li></ul><pre>@Component<br>export default struct NoSuk {<br>  @Consume(&#39;mainStack&#39;) pageStack: NavPathStack;<br><br>  build() {<br>    NavDestination() {<br>      Column() {<br>        Column() {<br>          SymbolGlyph($r(&#39;sys.symbol.xmark_circle_fill&#39;))<br>            .fontColor([Color.Red])<br>            .fontSize(24)<br>            .margin({ bottom: 8 });<br>          Text(&#39;No suk left.&#39;);<br>        }<br>        .size({ width: &#39;70%&#39;, height: &#39;70%&#39; })<br>        .alignItems(HorizontalAlign.Center)<br>        .justifyContent(FlexAlign.Center);<br>      }<br>      .size({ width: &#39;100%&#39;, height: &#39;100%&#39; })<br>      .backgroundColor(Color.Black)<br>      .alignItems(HorizontalAlign.Center)<br>      .justifyContent(FlexAlign.Center);<br>    }<br>    .hideTitleBar(true);<br>  }<br>}</pre><ul><li>And finally, Index.ets</li></ul><pre>import CardTransferSuccess from &#39;../components/CardTransferSuccess&#39;;<br>import NoSuk from &#39;../components/NoSuk&#39;;<br>import { SUKTable } from &#39;../rdb/tables/SukTable&#39;;<br>import HceHandler from &#39;../util/HceHandler&#39;;<br>import CardVM from &#39;../viewmodel/CardVM&#39;;<br><br>@Entry<br>@Component<br>struct Index {<br>  @Provide(&#39;mainStack&#39;) pageStack: NavPathStack = new NavPathStack();<br>  @State cardVM: CardVM = CardVM.getInstance();<br><br>  onPageShow(): void {<br>    HceHandler.enable(this.getUIContext().getHostContext()!, &#39;foreground&#39;, this.pageStack);<br>  }<br><br>  build() {<br>    Navigation(this.pageStack) {<br>      Stack() {<br>        Column() {<br>          Blank().layoutWeight(1);<br><br>          Column() {<br>            Text(&#39;Contactless Payment&#39;)<br>              .fontSize(12);<br><br>            Text(this.cardVM.card?.name ?? &#39;Card Not Found&#39;)<br>              .fontSize(10);<br>          }<br>          .alignItems(HorizontalAlign.Start)<br>          .padding(6)<br>          .backgroundColor(Color.Gray)<br>          .width(&#39;100%&#39;)<br>          .borderRadius(8);<br><br>          Blank().layoutWeight(1);<br><br>          Row() {<br>            Button(&#39;Add Card&#39;)<br>              .layoutWeight(1)<br>              .height(30)<br>              .type(ButtonType.Normal)<br>              .backgroundColor(Color.Green)<br>              .onClick(() =&gt; {<br>                CardVM.getInstance().add({ name: &#39;My Lucky Card&#39; });<br>                SUKTable.add([{}, {}, {}, {}, {}]);<br>              });<br>            Button(&#39;Remove&#39;)<br>              .layoutWeight(1)<br>              .height(30)<br>              .type(ButtonType.Normal)<br>              .backgroundColor(Color.Red)<br>              .onClick(() =&gt; {<br>                CardVM.getInstance().delete();<br>                SUKTable.deleteAll();<br>              });<br>          };<br>        }<br>        .height(&#39;70%&#39;)<br>        .width(&#39;70%&#39;);<br>      }<br>      .backgroundColor(Color.Black)<br>      .height(&#39;100%&#39;)<br>      .width(&#39;100%&#39;);<br>    }<br>    .navDestination(this.pageMap)<br>    .hideToolBar(true);<br>  }<br><br>  @Builder<br>  pageMap(name: string) {<br>    if (name === &#39;CardTransferSuccess&#39;) {<br>      CardTransferSuccess();<br>    } else if (name === &#39;NoSuk&#39;) {<br>      NoSuk();<br>    }<br>  }<br>}</pre><p>You may wonder what this is:</p><pre>@State cardVM: CardVM = CardVM.getInstance();</pre><p>Don’t worry. I got you.</p><p>In normal cases, you might get card data from an API or from a mobile app. Here we will use mock data to simulate the process, and CardVM to handle app state.</p><pre>import { Card } from &#39;../model/Card&#39;;<br>import { CardTable } from &#39;../rdb/tables/CardTable&#39;;<br><br>@Observed<br>export default class CardVM {<br>  private static instance: CardVM;<br><br>  private constructor() {<br>  }<br><br>  public static getInstance(): CardVM {<br>    if (!CardVM.instance) {<br>      CardVM.instance = new CardVM();<br>    }<br>    return CardVM.instance;<br>  }<br><br>  card: Card | null = CardTable.get();<br><br>  add(card: Card) {<br>    this.delete()<br>    CardTable.add(card);<br>    this.card = card;<br>  }<br><br>  delete() {<br>    CardTable.delete()<br>    this.card = null;<br>  }<br>}</pre><h4>Result</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*9Vlc4bxVpYgNH3KZUZe0kQ.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*hkXOFsTWQjiHzJrOQMWlkg.png" /></figure><figure><img alt="" src="https://cdn-images-1.medium.com/max/500/1*t-Re6OH1mU8zzJsqSpKnOQ.png" /><figcaption>It is good, ain’t it?</figcaption></figure><h3>Conclusion</h3><p>That’s all for this article. We have covered host card emulation in smart wearables.</p><blockquote>You can check the project in GitHub: <a href="https://github.com/Explore-In-HMOS-Wearable/how-to-use-hce">https://github.com/Explore-In-HMOS-Wearable/how-to-use-hce</a></blockquote><p>See you all in new adventures. :)</p><p><strong>~ <em>Fortuna Favet Fortibus</em></strong></p><h3>References</h3><ul><li><a href="https://github.com/Explore-In-HMOS-Wearable/how-to-use-hce">GitHub - Explore-In-HMOS-Wearable/how-to-use-hce</a></li><li><a href="https://en.wikipedia.org/wiki/EMV">EMV - Wikipedia</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/nfc-hce-guide">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=60481c64c439" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/how-to-implement-hce-payment-in-smart-wearables-60481c64c439">How to Implement HCE Payment in Smart Wearables?</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What are the Open Capabilities of Huawei Wear Engine SDK?]]></title>
            <link>https://medium.com/huawei-developers/what-are-the-open-capabilities-of-huawei-wear-engine-sdk-010dd8892dc7?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/010dd8892dc7</guid>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[harmony-os]]></category>
            <category><![CDATA[wearable-technology]]></category>
            <category><![CDATA[communication]]></category>
            <dc:creator><![CDATA[Emine İNAN]]></dc:creator>
            <pubDate>Fri, 09 Jan 2026 11:11:06 GMT</pubDate>
            <atom:updated>2026-01-09T11:11:04.439Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*z9RfMCJZJJmH2dN5" /><figcaption>Photo by <a href="https://unsplash.com/@kennyleys?utm_source=medium&amp;utm_medium=referral">Kenny Leys</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p><strong>HUAWEI Wear Engine</strong> is a sophisticated integration framework designed for developers to bridge the gap between applications on mobile phones and wearable devices. By leveraging this engine, developers can establish a seamless bidirectional communication channel, allowing phone-based apps to send messages, notifications, and data to Huawei wearables, while simultaneously retrieving real-time device status.</p><h4>What are the Open Capabilities of Huawei Wear Engine SDK?</h4><p>The Huawei Wear Engine SDK offers a comprehensive set of open capabilities that allow developers to utilize the unique hardware and software features of both phones and wearables through a unified set of APIs. These capabilities are categorized into several core functions:</p><p><strong>🚩Basic device capabilities:</strong></p><ul><li><strong>Obtaining basic information about wearable devices</strong>: Phone applications can retrieve a list of paired Huawei wearable devices (specifically those supporting HarmonyOS). This includes identifying device names, types, and real-time status such as connection stability and app installation progress.</li><li><strong>App-to-app communications</strong>: A phone app and a wearable app can share messages and files (such as documents, images, and music).</li></ul><p><strong>🚩 Template-Based Notifications:</strong> Developers can push customized notifications from the phone to the wearable. These notifications are template-driven, allowing for personalized titles, body content, and interactive buttons.</p><p><strong>🚩 User Status Monitoring:</strong> Apps can query or subscribe to critical user data, such as heart rate alerts and wearing status, providing a more context-aware user experience.</p><p><strong>🚩 Device Identification:</strong> For authorized enterprise partners, the engine provides access to specific hardware identifiers, such as the device Serial Number (SN).</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/886/1*vJYeqtOSqeDDyNlOm2zErA.png" /><figcaption>Wear Engine Open Capabilities</figcaption></figure><h4>Real-World Scenarios Powered by Wear Engine Capabilities</h4><p>The integration of the Huawei Wear Engine SDK facilitates a more efficient interaction between mobile and wearable platforms. The following scenarios demonstrate how developers can apply these open capabilities to solve practical user needs:</p><p><strong>Distributed Collaboration Between Phones and Wearable Devices</strong></p><ul><li>Receiving notifications for important events on the wrist. For example, any notifications for schedules, medications, or tasks set in your phone app, can be synced to the wearable app. This means that users can receive and view important notifications on their wearable devices without their phones.</li><li>Introducing the brand new interactive experience to the wrist. For example, when users use a phone app to stream videos or listen to music, they can use their wearable device to perform remote controls such as pausing, skipping to the next video/song, or stopping playback.</li><li>Achieving real-time collaboration between the phone and the wearable device. For example, a user can start navigation from your phone app and then receive real-time instructions from the wearable app for when to turn left, go straight, or turn right. So the user doesn’t have to take out their phone or hold it in their hand to check the route as they navigate.</li></ul><p><strong>Device Virtualization Between Phones and Wearable Devices</strong></p><p>You can integrate the Wear Engine SDK into your phone app and do not need to develop the corresponding wearable app again.</p><ul><li>Obtaining real-time wearable status. For example, phone apps can obtain the wearable status of connection, wearing, and battery levels in real time, providing more value-added services for users.</li></ul><h3>Conclusion</h3><p>The <strong>HUAWEI Wear Engine SDK</strong> represents a pivotal advancement in cross-device synergy, offering developers a streamlined framework to bridge the gap between smartphones and wearables. By facilitating seamless data exchange and providing access to critical device insights, it empowers the creation of a more cohesive and responsive user ecosystem.</p><p>Ultimately, integrating Wear Engine allows developers to transcend the limitations of standalone applications. By leveraging these open capabilities, you can deliver highly personalized, context-aware services that enhance user convenience and expand the technical boundaries of your digital offerings.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/connectivity-Guides/service-introduction-0000000000018585#section14370945135215">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/connectivity-Guides/scenario-description-0000000000018593">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=010dd8892dc7" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/what-are-the-open-capabilities-of-huawei-wear-engine-sdk-010dd8892dc7">What are the Open Capabilities of Huawei Wear Engine SDK?</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[What are the HarmonyOS Build Process Configuration Files?]]></title>
            <link>https://medium.com/huawei-developers/what-are-the-harmonyos-build-process-configuration-files-a2efc01e05bf?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/a2efc01e05bf</guid>
            <category><![CDATA[harmonyos-next]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[deveco-studio]]></category>
            <category><![CDATA[ark-ts]]></category>
            <category><![CDATA[configuration]]></category>
            <dc:creator><![CDATA[Emine İNAN]]></dc:creator>
            <pubDate>Fri, 09 Jan 2026 11:09:31 GMT</pubDate>
            <atom:updated>2026-01-09T11:09:30.500Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*FSB9bZE0TKXjJWiv" /><figcaption>Photo by <a href="https://unsplash.com/@growtika?utm_source=medium&amp;utm_medium=referral">Growtika</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>The HarmonyOS build process is governed by a set of essential configuration files that define how an application is compiled, packaged, and deployed. Central to this architecture are hvigor-config.json5 and build-profile.json5. These files allow developers to manage the build environment, customize tool versions, and handle complex multi-product requirements. Understanding the hierarchy and specific roles of these configurations is fundamental to maintaining a stable and efficient development workflow in DevEco Studio.</p><h4>Understanding HarmonyOS Build Configuration Files</h4><p>The HarmonyOS build process relies on specific configuration files, primarily hvigor-config.json5 and build-profile.json5. these files define the environment, manage dependencies, and facilitate multi-product builds.</p><p><strong>Project-Wide Build Settings: hvigor-config.json5</strong></p><p>Located in the project’s hvigor directory, this file manages the build tool&#39;s execution environment. It contains:</p><ul><li><strong>Version Management:</strong> Build tool versions and script dependency specifications.</li><li><strong>Execution Policies:</strong> Build tool capabilities, including log levels and execution strategies.</li><li><strong>Runtime Parameters:</strong> Configuration for the Node.js runtime and additional parameters passed to build scripts.</li></ul><p><strong>Structural Configuration: build-profile.json5</strong></p><p>The build-profile.json5 file exists at both the project and module levels. It follows a hierarchical logic: if a buildOption is defined in both, the module-level setting takes precedence.</p><p><strong>1. Project-Level Configuration</strong></p><p>Stored in the root directory, this file defines the overarching application structure:</p><ul><li><strong>Project Architecture:</strong> Lists all modules, their names, and physical paths.</li><li><strong>App Basics:</strong> Specifies the application name, SDK versions, and signature information.</li><li><strong>Multi-Product Targets:</strong> Configures specific product definitions and buildMode settings.</li></ul><p><strong>2. Module-Level Configuration</strong></p><p>Each module contains its own profile to manage local build requirements:</p><ul><li><strong>API and Targets:</strong> Defines the API model type and specific targets for multi-product builds.</li><li><strong>Build Customization:</strong> Manages compilation and packaging settings for ArkTS/C++ source code and project resources.</li></ul><h3>Conclusion</h3><p>Efficiently managing hvigor-config.json5 and build-profile.json5 is critical for scaling HarmonyOS applications. While the former stabilizes the build environment, the latter provides the structural flexibility needed for multi-module and multi-product development. By mastering these configuration files, developers can ensure a predictable, reproducible, and highly customized build process within DevEco Studio.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-configuration-file-overview">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-set-options">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-build-profile-app">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-build-profile">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=a2efc01e05bf" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/what-are-the-harmonyos-build-process-configuration-files-a2efc01e05bf">What are the HarmonyOS Build Process Configuration Files?</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Securing Apps with Crypto Architecture Kit in HarmonyOS Next]]></title>
            <link>https://medium.com/huawei-developers/securing-apps-with-crypto-architecture-kit-in-harmonyos-next-9da89da3d56d?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/9da89da3d56d</guid>
            <category><![CDATA[ark-ts]]></category>
            <category><![CDATA[security]]></category>
            <category><![CDATA[mobile-app-development]]></category>
            <category><![CDATA[harmonyos-next]]></category>
            <category><![CDATA[huawei]]></category>
            <dc:creator><![CDATA[Emine İNAN]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 13:10:32 GMT</pubDate>
            <atom:updated>2026-01-07T13:10:30.777Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*gQKZ9NW0ARd9i-B2" /><figcaption>Photo by <a href="https://unsplash.com/@sortino?utm_source=medium&amp;utm_medium=referral">Joshua Sortino</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p>In the modern app ecosystem, data privacy isn’t just a feature it’s a requirement. Whether you are handling sensitive user data or securing communications between a client and a server, choosing the right cryptographic tools is essential.</p><p>Enter the <strong>Crypto Architecture Kit</strong>, a robust framework designed to provide high-level cryptographic functionalities while shielding developers from the complexities of underlying third-party libraries.</p><h4>What is the Crypto Architecture Kit?</h4><p>The Crypto Architecture Kit acts as an abstraction layer for cryptographic operations. By providing a unified interface, it allows developers to implement complex security features without needing to worry about the implementation differences of various third-party libraries. Currently, the kit primarily leverages <strong>OpenSSL</strong> to deliver its capabilities.</p><p><strong>Core Capabilities</strong></p><p>The kit covers nearly every standard cryptographic requirement:</p><ul><li><strong>Encryption &amp; Decryption:</strong> Protecting data at rest and in transit.</li><li><strong>Signing &amp; Verification:</strong> Ensuring data integrity and authenticity.</li><li><strong>Message Authentication Codes (MAC):</strong> Verifying that a message has not been tampered with.</li><li><strong>Key Agreement &amp; Derivation:</strong> Securely establishing and expanding cryptographic keys.</li><li><strong>Message Digest (MD):</strong> Generating unique fingerprints for your data.</li><li><strong>Random Number Generation:</strong> Creating secure, unpredictable seeds for security protocols.</li></ul><p><strong>Understanding the Fundamentals</strong></p><p>To use the kit effectively, it is vital to distinguish between the two primary types of cryptography it supports:</p><p><strong>1. Symmetric Keys</strong></p><p>In symmetric cryptography, a single key is used for both encryption and decryption. It is fast and efficient for bulk data, but it requires both the sender and receiver to share the same secret key securely.</p><p><strong>2. Asymmetric Keys</strong></p><p>Asymmetric cryptography uses a key pair: a <strong>Public Key</strong> and a <strong>Private Key</strong>.</p><ul><li><strong>For Encryption:</strong> Data encrypted with a public key can only be decrypted by the corresponding private key.</li><li><strong>For Signing:</strong> A user signs data with their private key, and anyone with the public key can verify that the signature is authentic.</li></ul><p><strong>Constraints and Best Practices</strong></p><p>While the Crypto Architecture Kit is powerful, developers must be aware of its specific operational constraints to avoid security pitfalls:</p><ul><li><strong>No Multi-threading:</strong> The kit does not currently support multi-thread concurrent operations. Ensure your cryptographic calls are handled within a single-thread context or managed via serial queues.</li><li><strong>Algorithm Selection:</strong> While the kit supports a wide range of algorithms, not all are created equal. Legacy algorithms like <strong>MD5</strong> are available but are <strong>not recommended</strong> for high-security scenarios due to vulnerabilities. Always choose modern specifications (like AES or RSA-2048+) based on your specific service requirements.</li><li><strong>Key Management:</strong> A crucial distinction to remember is that this kit provides <strong>operations</strong>, not <strong>storage</strong>. It is designed for keys held in memory (like temporary session keys).</li></ul><p><strong>Pro Tip:</strong> If your application requires long-term, secure key storage (such as protecting a master key at the hardware level), you should pair this kit with the <strong>Universal Keystore Kit</strong>.</p><h4>Why Use It?</h4><p>The primary advantage of the Crypto Architecture Kit is the <strong>elevation of the developer experience.</strong> By providing a consistent API, it reduces the boilerplate code usually required to interface directly with libraries like OpenSSL. This leads to cleaner codebases, fewer implementation errors, and more secure applications.</p><h3>Conclusion</h3><p>The <strong>Crypto Architecture Kit</strong> represents a significant step forward for developers who need to balance robust security with development efficiency. By abstracting the complexities of OpenSSL and providing a unified interface for everything from MAC generation to key agreement, it allows you to focus on building features rather than wrestling with cryptographic specifications.</p><p>However, remember that great power comes with the responsibility of implementation:</p><ul><li><strong>Be Mindful of Threading:</strong> Keep your operations synchronous and avoid concurrent calls to prevent errors.</li><li><strong>Prioritize Modern Standards:</strong> Steer clear of legacy algorithms like MD5 in favor of more secure alternatives provided within the kit.</li><li><strong>Layer Your Security:</strong> Use the Crypto Architecture Kit for the “work” (encryption/signing) and the <strong>Universal Keystore Kit</strong> for the “vault” (long-term key storage).</li></ul><p>By understanding these boundaries and leveraging the kit’s comprehensive capability scope, you can ensure your application’s data remains uncompromised and your user experience stays seamless.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/crypto-architecture-kit-intro">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/crypto-key-generation-conversion-overview">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/crypto-encryption-decryption">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/crypto-sign-sig-verify">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9da89da3d56d" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/securing-apps-with-crypto-architecture-kit-in-harmonyos-next-9da89da3d56d">Securing Apps with Crypto Architecture Kit in HarmonyOS Next</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[How to Generate ArkTSDoc in DevEco Studio]]></title>
            <link>https://medium.com/huawei-developers/how-to-generate-arktsdoc-in-deveco-studio-5bd604b1524a?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/5bd604b1524a</guid>
            <category><![CDATA[ark-ts]]></category>
            <category><![CDATA[documentation]]></category>
            <category><![CDATA[deveco-studio]]></category>
            <category><![CDATA[harmonyos-next]]></category>
            <category><![CDATA[huawei]]></category>
            <dc:creator><![CDATA[Emine İNAN]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 13:09:13 GMT</pubDate>
            <atom:updated>2026-01-07T13:09:12.357Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*YfLzVu7dFJ25f5vk" /><figcaption>Photo by <a href="https://unsplash.com/@mvdheuvel?utm_source=medium&amp;utm_medium=referral">Maarten van den Heuvel</a> on <a href="https://unsplash.com?utm_source=medium&amp;utm_medium=referral">Unsplash</a></figcaption></figure><h3>Introduction</h3><p><strong>DevEco Studio</strong> simplifies the documentation process through its integrated <strong>Generate ArkTSDoc</strong> feature. This tool enables developers to efficiently produce structured reference documentation for exported variables, methods, APIs, and classes directly from the source code. By automating the extraction of technical details, it ensures that project interfaces are accurately documented and easily accessible, fostering better maintainability and collaboration within the HarmonyOS ecosystem.</p><h4>Implementation and Key Requirements</h4><ol><li>On the menu bar, choose <strong>Tools</strong> &gt; <strong>Generate ArkTSDoc…</strong> to open the <strong>Generate ArkTSDoc</strong> dialog box.</li><li>Set the scope for the documentation generation and the output directory.</li></ol><figure><img alt="" src="https://cdn-images-1.medium.com/max/351/0*Ymi4KukOQcWeRKEs.png" /><figcaption>Generate ArkTSDoc</figcaption></figure><p>3. To enable the ArkTSDoc documentation to automatically open in the browser after being generated, select <strong>Open generated documentation in browser</strong>. When you are done, click <strong>Generate</strong> to start the scanning and documentation generation.</p><p>The left panel of the generated ArkTSDoc mirrors your project’s directory structure, while the right panel allows you to navigate to specific variables, methods, APIs, or classes in the documentation.</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/0*qNrJCNyF7ybHzmBG.png" /><figcaption>ArktsDoc</figcaption></figure><p>If the <strong>Open generated documentation in browser</strong> option is not selected, a notification will pop up in the lower right corner of DevEco Studio after the documentation is generated. You can click <strong>Go to Folder</strong> to navigate to the generated ArkTSDoc folder and open the <strong>index.html</strong> file in a browser to view the documentation.</p><p><strong>Key Technical Constraints:</strong></p><p><strong>Supported Formats:</strong> Documentation can be generated for .ets, .ts, .js, and .md files.</p><p><strong>Export Visibility:</strong> Only <strong>exported</strong> variables, methods, APIs, and classes are included; internal, unexported items are automatically excluded.</p><p><strong>Structure Preservation:</strong> The generated ArkTSDoc maintains the original project or directory hierarchy for seamless navigation.</p><h3>Conclusion</h3><p>The <strong>Generate ArkTSDoc</strong> feature in DevEco Studio is an essential tool for maintaining high-quality technical standards in HarmonyOS development. By automating the transition from source code to professional documentation, it eliminates manual overhead and ensures that your API remains clear and navigable. Adopting this workflow not only improves internal project organization but also provides external collaborators with the structured information necessary for successful integration.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-arktsdoc">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-arktsdoc-generation">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=5bd604b1524a" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/how-to-generate-arktsdoc-in-deveco-studio-5bd604b1524a">How to Generate ArkTSDoc in DevEco Studio</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Gesture Events in HarmonyOS Next -ArkTS]]></title>
            <link>https://medium.com/huawei-developers/gesture-events-in-harmonyos-next-arkts-9d387c8a11c1?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/9d387c8a11c1</guid>
            <category><![CDATA[wearables]]></category>
            <category><![CDATA[gesture-handler]]></category>
            <category><![CDATA[ark-ts]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[harmonyos-next]]></category>
            <dc:creator><![CDATA[Mustafa şahin]]></dc:creator>
            <pubDate>Wed, 07 Jan 2026 07:48:59 GMT</pubDate>
            <atom:updated>2026-01-07T07:48:53.345Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*oWkjH8RBUMGe9zhDcLr71A.png" /><figcaption>Gesture Events in HarmonyOS Next — ArkTS</figcaption></figure><h3>Introduction</h3><p>In this article, we will talk about the Gesture events for HarmonyOS Next.</p><p>Gesture events in HarmonyOS ArkTS enable developers to handle user interactions such as taps, long presses, pans, pinches, and swipes.</p><p>These events are essential for creating responsive and intuitive UI components. This document provides a detailed guide on implementing gesture binding, single gestures, combined gestures, multi-level gesture events, and gesture judgment logic.s</p><h4><strong>Understanding the ArkTS Gesture Events</strong></h4><p>HarmonyOS ArkTS provides a flexible gesture system that allows developers to bind gestures to UI components, prioritize gestures, and handle complex interactions. Key concepts include:</p><ul><li><strong>Gesture Binding</strong>: Associating gestures with components using .gesture(), .priorityGesture(), and .parallelGesture().</li><li><strong>Single Gestures</strong>: Individual interactions like taps, long presses, pans, pinches, and swipes.</li><li><strong>Combined Gestures</strong>: Layered or simultaneous interactions using GestureGroup with modes like Sequence, Parallel, and Exclusive.</li><li><strong>Multi-level Gesture Events</strong>: Handling gestures in nested UI hierarchies with responseRegion and hitTestBehavior.</li><li><strong>Gesture Judgment</strong>: Resolving conflicts and prioritizing gestures using APIs like onGestureJudgeBegin and shouldBuiltInRecognizerParallelWith.</li></ul><h3>Implementation Steps</h3><p><strong>Gesture Binding</strong></p><p><strong>.gesture()</strong>: Bind a single gesture to a component.</p><p><strong>Example:</strong> Tap, LongPress, Pan, Pinch, Rotation, or Swipe gestures.</p><p><strong>.priorityGesture()</strong>: Prioritize parent gestures over child components.</p><p><strong>Example:</strong> Ensure parent components respond to gestures even if child components are overlapping.</p><p><strong>.parallelGesture()</strong>: Enable both parent and child components to respond to the same gesture.</p><p><strong>Example:</strong> Simultaneous pan gestures on nested components.</p><h4>Single Gesture</h4><ol><li><strong>Define the gesture type</strong>: Use TapGesture, LongPressGesture, PanGesture, RotationGesture,SwipeGesture .</li><li><strong>Bind the gesture</strong>: Use .gesture() to associate the gesture with a component.</li><li><strong>Implement action logic</strong>: Use .onAction() to define the behavior triggered by the gesture.</li></ol><h4>Example code snippets:</h4><p><strong>Tap Gesture</strong></p><pre>Text(&quot;Tap Me&quot;)  <br>  .gesture(TapGesture({ count: 2 })  <br>    .onAction(() =&gt; {  <br>      console.info(&quot;Double tap detected&quot;);  <br>    })  <br>  )</pre><p><strong>Long Press Gesture</strong></p><pre>Text(&quot;LongPress OnAction:&quot; + this.count)  <br>  .gesture(LongPressGesture({ repeat: true })  <br>    .onAction(() =&gt; {  <br>      this.count++;  <br>    })  <br>  )</pre><p><strong>Pan Gesture</strong></p><pre>Text(&quot;PanGesture Offset:\nX: &quot; + this.offsetX + &quot;\nY: &quot; + this.offsetY)  <br>  .gesture(PanGesture()  <br>    .onActionUpdate((event) =&gt; {  <br>      this.offsetX = event.offsetX;  <br>      this.offsetY = event.offsetY;  <br>    })  <br>  )</pre><p><strong>Pinch Gesture</strong></p><pre>Column()  <br>  .scale({ x: this.scaleValue, y: this.scaleValue })  <br>  .gesture(PinchGesture({ fingers: 3 })  <br>    .onActionUpdate((event) =&gt; {  <br>      this.scaleValue = event.scale;  <br>    })  <br>  )</pre><p><strong>Rotation Gesture</strong></p><pre>Text(&quot;RotationGesture angle:&quot; + this.angle)  <br>  .rotate({ angle: this.angle })  <br>  .gesture(RotationGesture()  <br>    .onActionUpdate((event) =&gt; {  <br>      this.angle = event.angle;  <br>    })  <br>  )</pre><p><strong>Swipe Gesture</strong></p><pre>Column()  <br>  .rotate({ angle: this.rotateAngle })  <br>  .gesture(SwipeGesture({ direction: SwipeDirection.Vertical })  <br>    .onAction((event) =&gt; {  <br>      this.rotateAngle = event.angle;  <br>    })  <br>  )</pre><h4>Combined Gestures</h4><ol><li><strong>Use </strong><strong>GestureGroup</strong>: Combine multiple gestures with GestureMode (Sequence, Parallel, Exclusive).</li><li><strong>Define parameters</strong>: Adjust count, distance, speed, fingers, etc., to customize gesture behavior.</li><li><strong>Handle recognition</strong>: Use .onAction() or .onActionUpdate() to process gesture events.</li></ol><h4>Combined Gestures Examples</h4><p>Parallel Recognition</p><pre>gesture(GestureGroup(GestureMode.Parallel,  <br>  TapGesture({ count: 1 })  <br>    .onAction(() =&gt; { this.count1++; }),  <br>  TapGesture({ count: 2 })  <br>    .onAction(() =&gt; { this.count2++; })  <br>))</pre><p>Exclusive Recognition</p><pre>gesture(GestureGroup(GestureMode.Exclusive,  <br>  TapGesture({ count: 1 })  <br>    .onAction(() =&gt; { this.count1++; }),  <br>  TapGesture({ count: 2 })  <br>    .onAction(() =&gt; { this.count2++; })  <br>))</pre><h4>Multi-level Gesture Events</h4><ol><li><strong>Use </strong><strong>responseRegion</strong>: Control the touch target area of a component.</li><li><strong>Set </strong><strong>hitTestBehavior</strong>: Define how components interact with touch events (Block, Transparent, None).</li><li><strong>Manage priority</strong>: Use .priorityGesture() or .parallelGesture() to control gesture priority.</li></ol><h4>Gesture Judgment</h4><ol><li><strong>Use </strong><strong>onGestureJudgeBegin</strong>: Dynamically decide whether to trigger a gesture based on component state.</li><li><strong>Use </strong><strong>shouldBuiltInRecognizerParallelWith</strong>: Control parallel gesture recognition between nested components.</li><li><strong>Adjust recognizer states</strong>: Enable or disable gesture recognizers based on the boundary.</li></ol><h4>Gesture Judgment Examples</h4><p>shouldBuiltInRecognizerParallelWith</p><pre>.shouldBuiltInRecognizerParallelWith((current, others) =&gt; {  <br>  for (let i = 0; i &lt; others.length; i++) {  <br>    let target = others[i].getEventTargetInfo();  <br>    if (target.getId() == &quot;inner&quot; &amp;&amp; others[i].isBuiltIn() &amp;&amp; others[i].getType() == GestureControl.GestureType.PAN_GESTURE) {  <br>      return others[i];  <br>    }  <br>  }  <br>  return undefined;  <br>})</pre><p>onGestureRecognizerJudgeBegin</p><pre>.onGestureRecognizerJudgeBegin((event, current, others) =&gt; {  <br>  let target = current.getEventTargetInfo();  <br>  if (target.getId() == &quot;outer&quot; &amp;&amp; current.isBuiltIn() &amp;&amp; current.getType() == GestureControl.GestureType.PAN_GESTURE) {  <br>    for (let i = 0; i &lt; others.length; i++) {  <br>      let target = others[i].getEventTargetInfo() as ScrollableTargetInfo;  <br>      if (target instanceof ScrollableTargetInfo &amp;&amp; target.getId() == &quot;inner&quot;) {  <br>        let panEvent = event as PanGestureEvent;  <br>        this.childRecognizer.setEnabled(true);  <br>        this.currentRecognizer.setEnabled(false);  <br>        if (target.isEnd()) {  <br>          if (panEvent &amp;&amp; panEvent.offsetY &lt; 0) {  <br>            this.childRecognizer.setEnabled(false);  <br>            this.currentRecognizer.setEnabled(true);  <br>          }  <br>        }  <br>      }  <br>    }  <br>  }  <br>  return GestureJudgeResult.CONTINUE;  <br>})</pre><h4>Considerations</h4><p><strong>Conflict Resolution</strong>:</p><ul><li>Use responseRegion and hitTestBehavior to avoid unintended behavior in nested hierarchies.</li><li>Test thoroughly to resolve conflicts between parent and child components.</li></ul><p><strong>Performance</strong>:</p><ul><li>Optimize gesture parameters (e.g., distance, speed, count) for smooth interactions.</li><li>Avoid overusing gestures in complex UIs to prevent performance issues.</li></ul><p><strong>User Experience</strong>:</p><ul><li>Ensure gestures align with user expectations (e.g., long press feedback).</li><li>Provide visual or auditory feedback for gestures like taps and swipes.</li></ul><p><strong>Testing</strong>:</p><ul><li>Always test on real devices to ensure expected behavior in nested hierarchies.</li><li>Validate gesture recognition under different conditions (e.g., overlapping components, touch targets).</li></ul><h3>Conclusion</h3><p>HarmonyOS ArkTS provides a flexible gesture system for creating rich, interactive UIs with precise control over taps, swipes, and multi-touch. It helps you connect these gestures to buttons and other parts of your app. You can also decide which gesture is more important when there are two together. This system is very useful for making apps that feel natural and fun for people to use.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-gesture-events-binding">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-gesture-events-single-gesture">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-gesture-events-combined-gestures">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/arkts-gesture-events-gesture-judge">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=9d387c8a11c1" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/gesture-events-in-harmonyos-next-arkts-9d387c8a11c1">Gesture Events in HarmonyOS Next -ArkTS</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
        <item>
            <title><![CDATA[Implementing Environment Switching in HarmonyOS ArkTS with Build Modes]]></title>
            <link>https://medium.com/huawei-developers/implementing-environment-switching-in-harmonyos-arkts-with-build-modes-88d631bc0977?source=rss----25443cf6c73---4</link>
            <guid isPermaLink="false">https://medium.com/p/88d631bc0977</guid>
            <category><![CDATA[harmony-os]]></category>
            <category><![CDATA[deveco-studio]]></category>
            <category><![CDATA[harmonyos-next]]></category>
            <category><![CDATA[huawei]]></category>
            <category><![CDATA[wearables]]></category>
            <dc:creator><![CDATA[Bilal Basboz]]></dc:creator>
            <pubDate>Tue, 06 Jan 2026 07:40:14 GMT</pubDate>
            <atom:updated>2026-01-06T07:40:11.623Z</atom:updated>
            <content:encoded><![CDATA[<figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*hZLGoSfqh_Cj7S7j_6jSzg.png" /><figcaption>AI Generated image (ChatGPT)</figcaption></figure><h3>Introduction</h3><p>Modern applications rarely run in a single environment. Most teams need <strong>development</strong>, <strong>staging</strong>, and <strong>production</strong> setups — each with different API endpoints, logging rules, and analytics behavior.<br>The real challenge is doing this <strong>cleanly</strong>, without scattering if (debug) conditions across your code or maintaining multiple projects.</p><p>In this article, we’ll walk through a <strong>production-ready approach</strong> to environment switching in <strong>HarmonyOS</strong> using <strong>Build Modes</strong> and <strong>ArkTS</strong>, fully aligned with how DevEco Studio is designed to work.</p><h4>Why Build Modes Are the Right Tool</h4><p>DevEco Studio supports <strong>Build Modes</strong> such as debug, stage, and release.<br> Each build mode can inject <strong>compile-time constants</strong> into your app via build-profile.json5.</p><p>These values are:</p><ul><li>Defined <strong>once</strong> at the project level</li><li>Automatically compiled into a generated BuildProfile</li><li>Accessible directly from the ArkTS code</li><li>Switched <strong>without touching application logic</strong></li></ul><p>This gives us environment isolation similar to <em>Gradle BuildConfig</em> on Android or <em>flavors</em> — but with a cleaner ArkTS-native flow.</p><h4>What We Want to Achieve</h4><p>Our goal is to switch environments <strong>only by selecting a Build Mode</strong>, while ArkTS code automatically receives the correct configuration:</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/1024/1*xVB5K3KZ2HT8uihkBMNFZg.png" /><figcaption>Build Field values for each Build Mode</figcaption></figure><p>No manual edits. No runtime flags. No branching logic.</p><h4>Step 1: Define Environment Variables in Build Modes</h4><p>Open your <strong>project-level</strong> build-profile.json5 and define environment-specific values under buildModeSet.</p><pre>&quot;buildModeSet&quot;: [<br>  {<br>    &quot;name&quot;: &quot;debug&quot;,<br>    &quot;buildOption&quot;: {<br>      &quot;debuggable&quot;: true,<br>      &quot;arkOptions&quot;: {<br>        &quot;buildProfileFields&quot;: {<br>          &quot;API_URL&quot;: &quot;dev.example.com&quot;,<br>          &quot;ENABLE_LOGGING&quot;: true,<br>          &quot;ENABLE_ANALYTICS&quot;: false<br>        }<br>      }<br>    }<br>  },<br>  {<br>    &quot;name&quot;: &quot;stage&quot;,<br>    &quot;buildOption&quot;: {<br>      &quot;debuggable&quot;: false,<br>      &quot;arkOptions&quot;: {<br>        &quot;buildProfileFields&quot;: {<br>          &quot;API_URL&quot;: &quot;stage.example.com&quot;,<br>          &quot;ENABLE_LOGGING&quot;: true,<br>          &quot;ENABLE_ANALYTICS&quot;: true<br>        }<br>      }<br>    }<br>  },<br>  {<br>    &quot;name&quot;: &quot;release&quot;,<br>    &quot;buildOption&quot;: {<br>      &quot;debuggable&quot;: false,<br>      &quot;arkOptions&quot;: {<br>        &quot;buildProfileFields&quot;: {<br>          &quot;API_URL&quot;: &quot;prod.example.com&quot;,<br>          &quot;ENABLE_LOGGING&quot;: false,<br>          &quot;ENABLE_ANALYTICS&quot;: true<br>        }<br>      }<br>    }<br>  }<br>]</pre><p>Each build mode now represents a <strong>fully isolated environment</strong>.</p><h4>Step 2: Ensure Modules Are Linked to a Product</h4><p>Your modules must be attached to a product for Build Modes to apply correctly.</p><pre>&quot;modules&quot;: [<br>  {<br>    &quot;name&quot;: &quot;entry&quot;,<br>    &quot;srcPath&quot;: &quot;./entry&quot;,<br>    &quot;targets&quot;: [<br>      {<br>        &quot;name&quot;: &quot;default&quot;,<br>        &quot;applyToProducts&quot;: [&quot;default&quot;]<br>      }<br>    ]<br>  }<br>]</pre><p>This is a common oversight — without it, Build Modes may not propagate.</p><h4>Step 3: Build and Inspect the Generated BuildProfile</h4><p>After selecting a Build Mode (for example, <strong>stage</strong>) and building the project,</p><figure><img alt="" src="https://cdn-images-1.medium.com/max/463/1*KgbnrZMRpMBy-fqoEXLNRg.png" /><figcaption>DevEco Studio Build Mode Selection Window</figcaption></figure><p>DevEco Studio generates:</p><pre>entry/build/default/generated/profile/default/BuildProfile.ets</pre><p>Example output:</p><pre>export const BUILD_MODE_NAME = &#39;stage&#39;;<br>export const DEBUG = false;<br>export const API_URL = &#39;stage.example.com&#39;;<br>export const ENABLE_LOGGING = true;<br>export const ENABLE_ANALYTICS = true;<br><br>export default class BuildProfile {<br>  static readonly API_URL = API_URL;<br>  static readonly ENABLE_LOGGING = ENABLE_LOGGING;<br>  static readonly ENABLE_ANALYTICS = ENABLE_ANALYTICS;<br>}</pre><p>⚠️ <strong>Important</strong><br>This file is <strong>auto-generated</strong>.<br>Do <strong>not</strong> edit it manually — its contents change based on the selected Build Mode.</p><h4>Step 4: Access Environment Variables in ArkTS</h4><p>Using the generated values in ArkTS is straightforward.</p><pre>import BuildProfile from &#39;BuildProfile&#39;;<br><br>@Entry<br>@Component<br>struct Index {<br>  @State apiUrl: string = BuildProfile.API_URL;<br>  @State loggingEnabled: boolean = BuildProfile.ENABLE_LOGGING;<br>  @State analyticsEnabled: boolean = BuildProfile.ENABLE_ANALYTICS;<br><br>  aboutToAppear(): void {<br>    console.log(`API_URL: ${this.apiUrl}`);<br>    console.log(`ENABLE_LOGGING: ${this.loggingEnabled}`);<br>    console.log(`ENABLE_ANALYTICS: ${this.analyticsEnabled}`);<br>  }<br><br>  build() {<br>    // UI Components<br>  }<br>}</pre><figure><img alt="" src="https://cdn-images-1.medium.com/max/352/1*bNtZLgmX2UwYvjIRMyKimQ.png" /><figcaption>Log output result</figcaption></figure><p>No conditionals.<br>No runtime switches.<br>Everything is resolved at <strong>build time</strong>.</p><h4>Test Results</h4><figure><img alt="" src="https://cdn-images-1.medium.com/max/742/1*TZD2zphkCpcQTEETXS8i7A.png" /><figcaption>Result value for each Build Mode</figcaption></figure><p>✔ No code changes required<br>✔ BuildProfile regenerated correctly<br>✔ Safe and predictable configuration per environment</p><h4>Why This Approach Scales Well</h4><ul><li><strong>Clean architecture</strong> — no environment logic leaking into business code</li><li><strong>Safe releases</strong> — production builds cannot accidentally log or hit dev APIs</li><li><strong>Team-friendly</strong> — environment switching is a one-click operation in DevEco Studio</li><li><strong>CI/CD ready</strong> — build mode selection fits naturally into pipelines</li></ul><p>If you’re building <strong>wearable</strong> or <strong>multi-module</strong> HarmonyOS apps, this pattern scales effortlessly.</p><h3>Conclusion</h3><p>Build Modes + buildProfileFields provide one of the <strong>cleanest environment-switching mechanisms</strong> in the HarmonyOS ecosystem.<br>Once set up, your ArkTS code becomes environment-agnostic, and your builds become deterministic and safe.</p><p>If you’re coming from Android flavors or Flutter flavors — this is the HarmonyOS-native way to do it.</p><h3>References</h3><ul><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-compilation-options-customizing-guide#section192461528194916">Document</a></li><li><a href="https://developer.huawei.com/consumer/en/doc/harmonyos-guides/ide-hvigor-compilation-options-customizing-sample">Document</a></li></ul><img src="https://medium.com/_/stat?event=post.clientViewed&referrerSource=full_rss&postId=88d631bc0977" width="1" height="1" alt=""><hr><p><a href="https://medium.com/huawei-developers/implementing-environment-switching-in-harmonyos-arkts-with-build-modes-88d631bc0977">Implementing Environment Switching in HarmonyOS ArkTS with Build Modes</a> was originally published in <a href="https://medium.com/huawei-developers">Huawei Developers</a> on Medium, where people are continuing the conversation by highlighting and responding to this story.</p>]]></content:encoded>
        </item>
    </channel>
</rss>