DEV Community

HarmonyOS
HarmonyOS

Posted on

Persisting User Preferences with ArkData

Read the original article:Persisting User Preferences with ArkData

Requirement Description

The Preferences module in HarmonyOS provides APIs for processing data in the form of key-value (KV) pairs, including querying, modifying, and persisting data.
It is mainly used for storing lightweight data, such as user preferences (e.g., font size, language settings, night mode switch).

Preferences is designed for:

  • Storing small datasets in memory for fast access.
  • Persisting data to a file when needed.
  • Avoiding the complexity of databases for simple user settings.

Background Knowledge

  • Each Preferences instance is associated with a unique persistence file in the application sandbox.
  • Data can be stored in two formats:
    • XML (default): Recommended for single-process scenarios with small datasets.
    • GSKV (since API version 18): Supports multi-process concurrency and flushes changes to disk in real time.
  • Data operations are first cached in memory for speed. You must call flush() (in XML mode) to persist data to disk.
  • Preferences is not suitable for large datasets; recommended limit is ≤ 50 MB.

Implementation Steps

1.Import the module

import { preferences } from '@kit.ArkData';Copy codeCopy code
Enter fullscreen mode Exit fullscreen mode

2. Check storage type support (optional)

let isGskvSupported = preferences.isStorageTypeSupported(preferences.StorageType.GSKV);
console.info("Is GSKV supported: " + isGskvSupported);Copy codeCopy code
Enter fullscreen mode Exit fullscreen mode

3.Obtain a Preferences instance

let options: preferences.Options = { name: 'myStore' };
let dataPreferences = preferences.getPreferencesSync(this.context, options);
Enter fullscreen mode Exit fullscreen mode

4.Write data

dataPreferences.putSync('theme', 'dark');
dataPreferences.flush(() => console.info("Data persisted."));
Enter fullscreen mode Exit fullscreen mode

5.Read data

let theme = dataPreferences.getSync('theme', 'light');
console.info("Theme is: " + theme);
Enter fullscreen mode Exit fullscreen mode

6.Delete data

dataPreferences.deleteSync('theme');
Enter fullscreen mode Exit fullscreen mode

7.Subscribe to changes

let observer = (key: string) => console.info('Key changed: ' + key);
dataPreferences.on('change', observer);
Enter fullscreen mode Exit fullscreen mode

8. Delete Preferences instance

preferences.deletePreferences(this.context, options, (err) => {
  if (err) {
    console.error("Failed to delete preferences: " + err.message);
    return;
  }
  console.info("Preferences deleted.");
});
Enter fullscreen mode Exit fullscreen mode

Code Snippet / Configuration

Ready to use implementation example:

import { preferences } from '@kit.ArkData';
import { BusinessError } from '@kit.BasicServicesKit';
import { common } from '@kit.AbilityKit';

export class PreferencesUtil {
  private static instance: PreferencesUtil | null = null;
  private dataPreferences: preferences.Preferences | null = null;
  private readonly PREFERENCES_NAME = 'userSettings';
  private readonly DARK_MODE_KEY = 'isDarkMode';

  private constructor() {}

  static getInstance(): PreferencesUtil {
    if (PreferencesUtil.instance === null) {
      PreferencesUtil.instance = new PreferencesUtil();
    }
    return PreferencesUtil.instance;
  }

  async initPreferences(context: common.UIAbilityContext): Promise<boolean> {
    try {
      const options: preferences.Options = { name: this.PREFERENCES_NAME };
      this.dataPreferences = preferences.getPreferencesSync(context, options);
      console.info('Preferences initialized successfully');
      return true;
    } catch (err) {
      console.error('Failed to initialize preferences:', err);
      return false;
    }
  }

  getDarkModePreference(): boolean {
    if (!this.dataPreferences) {
      console.warn('Preferences not initialized');
      return false;
    }

    try {
      const isDarkMode = this.dataPreferences.getSync(this.DARK_MODE_KEY, false) as boolean;
      console.info('Loaded theme preference:', isDarkMode ? 'Dark' : 'Light');
      return isDarkMode;
    } catch (err) {
      console.error('Failed to load theme preference:', err);
      return false;
    }
  }

  saveDarkModePreference(isDarkMode: boolean, callback?: () => void): void {
    if (!this.dataPreferences) {
      console.warn('Preferences not initialized');
      return;
    }

    try {
      this.dataPreferences.putSync(this.DARK_MODE_KEY, isDarkMode);

      this.dataPreferences.flush((err: BusinessError) => {
        if (err) {
          console.error(`Failed to flush preferences. Code: ${err.code}, message: ${err.message}`);
        } else {
          console.info('Theme preference saved successfully:', isDarkMode ? 'Dark' : 'Light');
          if (callback) {
            callback();
          }
        }
      });
    } catch (err) {
      console.error('Failed to save theme preference:', err);
    }
  }

  toggleDarkMode(callback?: (newState: boolean) => void): void {
    const currentState = this.getDarkModePreference();
    const newState = !currentState;

    this.saveDarkModePreference(newState, () => {
      if (callback) {
        callback(newState);
      }
    });
  }
}
Enter fullscreen mode Exit fullscreen mode

Limitations or Considerations

  • Key constraints: must be a non-empty string ≤ 1024 bytes.
  • Value constraints:
    • Strings must be UTF-8 encoded, ≤ 16 MB.
    • Use Uint8Array for non-UTF-8 binary data.
  • XML limitations:
    • No concurrency safety in multi-process scenarios.
    • Higher memory usage as dataset grows (≤ 50 MB recommended).
    • Flush may block if used on main thread with large data.
  • GSKV limitations:
    • Not cross-platform.
    • All processes must belong to the same user group.
    • Do not call deletePreferences concurrently with other APIs.
  • Security: No built-in encryption. If needed, encrypt data first and store the ciphertext.

Related Documents

https://developer.huawei.com/consumer/en/doc/harmonyos-guides/data-persistence-by-preferences

Written by Hatice Akyel

Top comments (0)