Title: Harmony SDK Locale: en URL: https://sensorswave.com/en/docs/data-integration/client-sdks/harmony/ Description: Harmony SDK integration guide and API reference Sensors Wave HarmonyOS SDK is a client-side data collection and A/B testing tool designed for HarmonyOS applications, supporting API 10+ (HarmonyOS 4.0+) with Stage model architecture. ## Core Features The HarmonyOS SDK provides the following capabilities: - **Event Tracking**: Manually track custom events and user behaviors - **Auto-tracking**: Automatically capture app lifecycle events and page view events - **Click Collection**: Support automatic tracking of component click events - **User Identification**: Support anonymous user and logged-in user identity association - **User Properties Management**: Complete user property operations (set, append, increment, etc.) - **A/B Testing Integration**: Support feature gates and experiment variable retrieval - **Synchronous Calls**: All methods are synchronous except A/B testing, returning immediately - **Batch Sending**: Optimize network request performance with automatic batch event sending ## System Requirements - **DevEco Studio**: 4.0+ - **HarmonyOS API**: 10+ (HarmonyOS 4.0+) - **Language**: ArkTS - **App Model**: Stage model ## Installation ### OHPM Installation Install SDK via OHPM: ```bash ohpm install @sensorswave/harmony-sdk ``` Or add the dependency to your project's `oh-package.json5`: ```json5 { "dependencies": { "@sensorswave/harmony-sdk": "^latest" } } ``` ### Configure Permissions Add necessary permissions in the module's `src/main/module.json5`: ```json5 { "module": { "requestPermissions": [ { "name": "ohos.permission.INTERNET", "reason": "$string:internet_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } }, { "name": "ohos.permission.GET_NETWORK_INFO", "reason": "$string:network_info_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } }, { "name": "ohos.permission.GET_WIFI_INFO", "reason": "$string:wifi_info_permission_reason", "usedScene": { "abilities": ["EntryAbility"], "when": "always" } } ] } } ``` **Permission Description**: - `ohos.permission.INTERNET`: Required permission, allows app to access network - `ohos.permission.GET_NETWORK_INFO`: Required permission, allows app to get network information - `ohos.permission.GET_WIFI_INFO`: Optional permission, allows app to get WiFi information ## Quick Start ### Basic Initialization Initialize SDK in the `onCreate` method of `UIAbility`: ```typescript onCreate(want: Want, launchParam: AbilityConstant.LaunchParam): void { // Initialize SDK (synchronous call, returns immediately) Sensorswave.getInstance().init('your-source-token', { apiHost: 'https://your-api-host.com', context: this.context, debug: false, autoCapture: true }); } } ``` > **Tip**: Except for A/B testing APIs, all SDK methods are **synchronous calls** and return immediately. Events are automatically queued and sent in the background. ### Send Your First Event After initialization, you can start reporting events immediately: ```typescript // Track button click event Sensorswave.getInstance().trackEvent('button_click', { button_name: 'submit', page: 'home' }); ``` ## Configuration Options The second parameter of the `init` method is a configuration object supporting the following options: | Option | Type | Default | Description | |--------|------|---------|-------------| | `sourceToken` | `string` | *Required* | Data receiving token | | `apiHost` | `string` | *Required* | Data reporting server address | | `context` | `Context` | *Required* | App context (used to get app info) | | `debug` | `boolean` | `false` | Whether to enable debug mode | | `autoCapture` | `boolean` | `true` | Whether to auto-capture preset events (lifecycle events) | | `enableClickTrack` | `boolean` | `false` | Whether to enable click auto-tracking | | `batchSend` | `boolean` | `true` | Whether to use batch sending | | `enableAB` | `boolean` | `false` | Whether to enable A/B testing | | `abRefreshInterval` | `number` | `600000` | A/B testing config refresh interval (ms), default 10 minutes | ### Complete Configuration Example ```typescript Sensorswave.getInstance().init('your-source-token', { apiHost: 'https://your-api-host.com', context: this.context, debug: true, // Enable debug in development autoCapture: true, // Enable auto-tracking enableClickTrack: true, // Enable click tracking batchSend: true, // Batch send for performance enableAB: true, // Enable A/B testing abRefreshInterval: 10 * 60 * 1000 // Refresh A/B data every 10 minutes }); ``` ## API Methods ### Event Tracking All tracking methods are **synchronous calls** and return immediately. Events are automatically queued and sent in the background. #### trackEvent - Track Custom Events Send custom events with custom properties. **Parameters**: - `event` (string, required): Event name - `properties` (Record, optional): Event properties **Example**: ```typescript // E-commerce scenario: User views product Sensorswave.getInstance().trackEvent('product_view', { product_id: '12345', product_name: 'Wireless Bluetooth Headphones', category: 'Electronics', price: 299.00 }); ``` #### track - Advanced Event Tracking Send complete custom event data with more granular control. **Parameters**: - `eventData` (SensorswaveSendEvent, required): Complete event object - `event` (string, required): Event name - `properties` (Record, optional): Event properties - `time` (number, required): Timestamp (milliseconds) - `trace_id` (string, optional): Request trace ID - `anon_id` (string, optional): Anonymous user ID **Example**: ```typescript Sensorswave.getInstance().track({ event: 'product_view', properties: { product_id: '12345', source: 'search' }, time: Date.now(), trace_id: 'custom-trace-id', anon_id: 'custom-anon-id' }); ``` ### User Identification All user identity methods are **synchronous calls**. #### identify - Set Login User ID Set the current user's login ID and send a binding event to associate anonymous behavior with the identified user. **Parameters**: - `loginId` (string, required): User unique identifier (e.g., email, user ID, username) **Example**: ```typescript // Set user ID after user login Sensorswave.getInstance().identify('user_12345'); ``` **Complete Login Flow Example**: ```typescript async function handleUserLogin(email: string, password: string) { try { // Call login API const response = await fetch('/api/login', { method: 'POST', body: JSON.stringify({ email, password }) }); const { userId } = await response.json(); // Set user ID and associate anonymous behavior Sensorswave.getInstance().identify(userId); } catch (error) { console.error('Login failed:', error); } } ``` > **Important**: If the `loginId` contains sensitive information (such as phone numbers, email addresses, ID numbers, etc.), please encrypt it before transmission to avoid sending sensitive data in plain text. #### setLoginId - Set Login ID Only Set the current user's login ID without sending a binding event. **Parameters**: - `loginId` (string, required): User unique identifier **Example**: ```typescript Sensorswave.getInstance().setLoginId('user_12345'); ``` > **Important**: If the `loginId` contains sensitive information (such as phone numbers, email addresses, ID numbers, etc.), please encrypt it before transmission to avoid sending sensitive data in plain text. #### getAnonId - Get Anonymous User ID Get anonymous user ID. **Return Value**: string (synchronous) ```typescript const anonId = Sensorswave.getInstance().getAnonId(); ``` #### getLoginId - Get Login User ID Get login user ID. **Return Value**: string (synchronous) ```typescript const loginId = Sensorswave.getInstance().getLoginId(); ``` ### User Properties All user property methods are **synchronous calls** and return immediately. Properties are automatically queued and sent in the background. #### profileSet - Set User Properties Set user properties, overwriting if they already exist. **Parameters**: - `properties` (Record, required): User properties **Example**: ```typescript // Set user properties after registration Sensorswave.getInstance().profileSet({ name: 'John Doe', age: 30, plan: 'premium' }); ``` #### profileSetOnce - Set Only Once Set user properties only if they don't exist. Existing properties won't be overwritten. **Parameters**: - `properties` (Record, required): User properties **Example**: ```typescript // Record user first visit info (only once, won't overwrite) Sensorswave.getInstance().profileSetOnce({ signup_date: '2024-01-15', initial_referrer: 'google', initial_campaign: 'spring_sale' }); ``` #### profileIncrement - Increment Numeric Properties Increment or decrement numeric user properties. Only supports numeric properties. **Parameters**: - `properties` (Record, required): Numeric properties **Example**: ```typescript // Increment login count and points Sensorswave.getInstance().profileIncrement({ login_count: 1, points_earned: 100, purchases_count: 1 }); ``` #### profileAppend - Append to Array Properties Append new values to array-type user properties without deduplication. **Parameters**: - `properties` (Record, required): List properties **Example**: ```typescript // Append viewed categories and tags (no deduplication) Sensorswave.getInstance().profileAppend({ categories_viewed: ['Electronics', 'Phones'], tags: ['new_user', '2024Q1'] }); ``` #### profileUnion - Append to Set Properties (Deduplicated) Append new values to array-type user properties with automatic deduplication. **Parameters**: - `properties` (Record, required): List properties **Example**: ```typescript // First call Sensorswave.getInstance().profileUnion({ interests: ['Tech', 'Gaming'] }); // Second call, 'Tech' won't be added again Sensorswave.getInstance().profileUnion({ interests: ['Tech', 'Music'] }); ``` #### profileUnset - Delete Specific Properties Set specified user properties to null. **Parameters**: - `properties` (string | string[], required): Property name or array of property names **Example**: ```typescript // Delete single property Sensorswave.getInstance().profileUnset('temporary_campaign'); // Delete multiple properties Sensorswave.getInstance().profileUnset(['old_plan', 'expired_flag', 'temp_id']); ``` #### profileDelete - Delete All User Properties Delete all user properties for the current user. This operation cannot be undone. **Example**: ```typescript // Delete all user properties when user closes account Sensorswave.getInstance().profileDelete(); ``` > **Warning**: `profileDelete` deletes all user properties. Use with caution. #### User Properties Usage Example **Membership Upgrade Scenario**: ```typescript function handleMembershipUpgrade(newPlan: string) { // Update membership level and upgrade time Sensorswave.getInstance().profileSet({ plan: newPlan, upgrade_time: new Date().toISOString() }); // Increment upgrade count Sensorswave.getInstance().profileIncrement({ upgrade_count: 1 }); // Track upgrade event Sensorswave.getInstance().trackEvent('membership_upgrade', { from_plan: 'basic', to_plan: newPlan }); } ``` ### Common Properties All common property methods are **synchronous calls**. #### registerCommonProperties - Register Common Properties Register common properties that will be automatically included in all events. **Parameters**: - `properties` (CommonProps, required): Common properties **Example**: ```typescript const commonProps = new CommonProps(); commonProps.set('app_version', '1.0.0'); commonProps.set('environment', 'production'); commonProps.set('user_tier', 'vip'); Sensorswave.getInstance().registerCommonProperties(commonProps); ``` #### clearCommonProperties - Clear Common Properties Remove specified registered common properties. **Parameters**: - `propertyNames` (string[], required): Array of property names to remove **Example**: ```typescript Sensorswave.getInstance().clearCommonProperties(['app_version', 'user_tier']); ``` > **Note**: Common properties increase the data size of each event. Only add truly necessary common properties. ### A/B Testing HarmonyOS SDK has built-in A/B testing support for retrieving feature gate status and experiment variables. > **Note**: A/B testing methods are **asynchronous calls** requiring network requests. #### Enable A/B Testing ```typescript Sensorswave.getInstance().init('your-source-token', { apiHost: 'https://your-api-host.com', context: this.context, enableAB: true, // Enable A/B testing abRefreshInterval: 10 * 60 * 1000 // Config refresh interval (10 minutes) }); ``` #### checkFeatureGate - Check Feature Gate Check if a feature gate is enabled for the current user. **Parameters**: - `key` (string, required): Feature gate key **Return Value**: Promise **Example**: ```typescript // Check if feature is enabled async function initFeature() { const isEnabled = await Sensorswave.getInstance().checkFeatureGate('new_checkout_flow'); if (isEnabled) { // Show new feature showNewCheckout(); } else { // Show old feature showOldCheckout(); } } ``` #### getExperiment - Get Experiment Variables Get experiment variable data for the current user. **Parameters**: - `key` (string, required): Experiment key **Return Value**: Promise> **Example**: ```typescript // Get experiment config async function initExperiment() { const experiment = await Sensorswave.getInstance().getExperiment('homepage_layout'); if (experiment) { const { layout_type, color_scheme } = experiment; applyLayout(layout_type, color_scheme); } } ``` **Complete A/B Testing Example**: ```typescript // Homepage layout A/B test async function initHomepageLayout() { try { const experiment = await Sensorswave.getInstance().getExperiment('homepage_layout'); if (experiment) { const { layout_type, color_scheme, card_size } = experiment; updateHomepageLayout(layout_type, color_scheme, card_size); } } catch (error) { console.error('Failed to get experiment variables:', error); // Use default config } } // Initialize when page loads initHomepageLayout(); ``` > **Tip**: A/B testing features require configuration in the Sensors Wave backend. ## Auto-tracking ### Preset Events When `autoCapture: true` (default), SDK automatically captures the following preset events: **Lifecycle Events**: | Event Name | Trigger Time | Exclusive Properties | |------------|--------------|---------------------| | `$AppInstall` | First app launch | - | | `$AppStart` | App enters foreground | `$screen_name` | | `$AppEnd` | App enters background | `$screen_name`, `$event_duration` | | `$AppPageView` | Page displayed | `$screen_name`, `$title`, `$url` | | `$AppPageLeave` | Page hidden | `$screen_name`, `$title`, `$url`, `$event_duration` | **Click Events** (when `enableClickTrack` enabled): | Event Name | Trigger Time | Exclusive Properties | |------------|--------------|---------------------| | `$AppClick` | Control clicked | `$screen_name`, `$element_type`, `$element_id`, `$element_content`, `$element_position`, `$element_selector` | ### Navigation Support SDK uses UIObserver to listen for page navigation events, automatically triggering `$AppPageView` and `$AppPageLeave`. **Supported Navigation Methods**: | Navigation Method | Support | Description | Example | |-------------------|---------|-------------|---------| | **Router** | ✅ Fully supported | Traditional Router navigation | `router.pushUrl()` | | **Navigation** | ✅ Fully supported | HarmonyOS Navigation component | `NavDestination` | | **Component State** | ❌ Not supported | Page switching via `@State` variable | Conditional rendering | #### Router Navigation Example ```typescript // Page navigation router.pushUrl({ url: 'pages/DetailPage', params: { id: '123' } }); // Go back router.back(); ``` #### Navigation Component Example ```typescript @Entry @Component struct Index { private navStack: NavPathStack = new NavPathStack(); build() { Navigation(this.navStack) { // Page content } .navDestination((name: string, params: ESObject) => { // NavDestination page }) } } ``` > **Note**: If using component state navigation (e.g., `@State currentPage` to switch pages), SDK **cannot** automatically trigger page view events. Recommend using Router or Navigation component for fully automatic page tracking. ### $AppClick Event Details SDK uses UIObserver's `willClick` event to automatically capture component click events with a **blacklist mechanism** to filter input components. **Supported Component Types Examples**: - **Interactive Components**: Button, Toggle, Checkbox, Radio, Slider, Rating - **Container Components**: Stack, Row, Column, Flex, Grid, List, Scroll - **Media Components**: Image, Text, Span, ImageAnimator - **Navigation Components**: Navigator, Tabs, Divider, Panel > **Important Notes**: > 1. All components supporting click events are automatically captured (including custom clickable containers) > 2. For Dialog, Menu/Option, TabBar components, `$element_content` property may not be captured **Enable Method**: ```typescript Sensorswave.getInstance().init('your-source-token', { context: this.context, enableClickTrack: true // Enable click auto-tracking }); ``` > **Note**: `enableClickTrack` defaults to `false` and needs to be explicitly enabled. ## Preset Properties SDK automatically collects the following preset properties: **SDK Related**: - `$lib`: Fixed value "harmony" - `$lib_version`: SDK version number **Device Related**: - `$manufacturer`: Device manufacturer - `$model`: Device model - `$os`: Fixed value "HarmonyOS" - `$os_version`: System version number - `$screen_width`: Screen width - `$screen_height`: Screen height **App Specific**: - `$network_type`: Network type (wifi, 4G, 5G, etc.) - `$wifi`: Whether WiFi network - `$app_version`: App version number - `$app_id`: App ID - `$app_name`: App name - `$brand`: Device brand **General**: - `$timezone_offset`: Timezone offset - `$language`: System language ## Notes ### Synchronous Call Feature Except for A/B testing related APIs, all SDK methods are **synchronous calls** and return immediately. Events are automatically queued and sent in the background. This means: - Won't block UI thread - Can execute subsequent code immediately after call - Event data is automatically batched and sent ### Performance Optimization - Avoid calling `trackEvent` frequently in loops. Consider merging events or using throttling - Batch sending is enabled by default, effectively reducing network requests - Auto-tracking and click tracking add some performance overhead. Enable only when needed ### Data Security - Don't pass sensitive information (passwords, credit card numbers, ID numbers) in event properties - Source Token should be kept secure and not exposed in public code repositories - Ensure data reporting address uses HTTPS protocol ### Data Loss Risk - Client-side tracking may be affected by network environment, app being force closed, etc. There is a certain risk of data loss - For core business data, use server-side tracking. See [Tracking Strategy Selection](../tracking-strategy.mdx) ## FAQ ### Why is my data not being reported successfully? Check the following: 1. **Is Source Token correct**: Confirm the `sourceToken` parameter is correct 2. **Is data reporting address correct**: Confirm the `apiHost` configuration is correct 3. **Network permissions**: Confirm network permissions are added in `module.json5` 4. **Network connection**: Enable `debug: true` to check console logs 5. **System version**: Confirm device system version is API 10+ (HarmonyOS 4.0+) ### How to avoid sending data in development environment? ```typescript // Decide whether to initialize SDK based on build configuration const isDebug = true; // In actual projects, get based on build environment if (!isDebug) { Sensorswave.getInstance().init('your-source-token', { apiHost: 'https://your-api-host.com', context: this.context }); } ``` ## Related Documentation - [Tracking Strategy Selection](../tracking-strategy.mdx): Understand the pros and cons of server-side vs client-side tracking - [How to Correctly Identify Users](../user-identification.mdx): Best practices for user identification - [Data Model](../data-model.mdx): Understand Sensors Wave's data structure - [Events and Properties](../events-and-properties.mdx): Event design guidelines and best practices --- **Last Updated**: March 13, 2026 **License**: Apache-2.0