> ## Documentation Index
> Fetch the complete documentation index at: https://docs.appdna.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native Remote Config

> Read remote configuration values from the AppDNA Console

<Info>
  **Supported on:** iOS SDK `1.0.70+` · Android SDK `1.0.42+` · React Native SDK `1.0.7+` (New Architecture only)
</Info>

The remote config module delivers key-value pairs from the AppDNA Console to your app. Reads are served from an in-memory cache populated at SDK launch, so they return quickly without a network round trip.

## Get a Config Value

```typescript theme={null}
import { AppDNA } from '@appdna-ai/react-native-sdk';

const value = await AppDNA.remoteConfig.get('welcome_message');
const welcome = (value as string | undefined) ?? 'Hello!';
```

`get` returns `Promise<unknown>` -- the value type matches whatever you configured in the Console (string, number, boolean, or JSON object). Cast to the type you expect and supply a fallback for the `undefined` case.

### Reading Other Types

```typescript theme={null}
const retries = ((await AppDNA.remoteConfig.get('max_retries')) as number | undefined) ?? 3;
const discount = ((await AppDNA.remoteConfig.get('discount_rate')) as number | undefined) ?? 0.1;
const promoOn = ((await AppDNA.remoteConfig.get('show_promo')) as boolean | undefined) ?? false;

// JSON values arrive as plain JS objects across the bridge.
const raw = await AppDNA.remoteConfig.get('hero_banner');
const banner = typeof raw === 'object' && raw !== null
  ? (raw as Record<string, unknown>)
  : null;
```

## Get All Values

```typescript theme={null}
const all = await AppDNA.remoteConfig.getAll();
console.log(`Loaded ${Object.keys(all).length} config keys`);
```

`getAll` returns a `Record<string, unknown>` containing every key the SDK currently has cached.

## Force a Refresh

```typescript theme={null}
await AppDNA.remoteConfig.refresh();
```

`refresh` pulls the latest config from the server. The SDK already refreshes automatically (see below) -- call this explicitly only when you need fresh values right now (e.g. after a user changes their preferences in another tab).

## Module Access

```typescript theme={null}
const remoteConfig = AppDNA.remoteConfig;
```

### Module Methods

| Method      | Signature                                     | Description                                                                                                                                                      |
| ----------- | --------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `get`       | `get(key: string): Promise<unknown>`          | Read a single config value (cast to the expected type)                                                                                                           |
| `getAll`    | `getAll(): Promise<Record<string, unknown>>`  | Read every cached key/value pair                                                                                                                                 |
| `refresh`   | `refresh(): Promise<void>`                    | Force a refresh from the server                                                                                                                                  |
| `onChanged` | `onChanged(callback: () => void): () => void` | Register a callback that fires when values change. The callback receives no arguments — re-read the config keys you care about. Returns an unsubscribe function. |

## Typed Helpers

If you prefer typed accessors at the call site, add a small helper module. This keeps the SDK surface small while giving your codebase a typed feel.

```typescript theme={null}
import { AppDNA } from '@appdna-ai/react-native-sdk';

export const TypedRemoteConfig = {
  async getString(key: string, fallback = ''): Promise<string> {
    const v = await AppDNA.remoteConfig.get(key);
    return typeof v === 'string' ? v : fallback;
  },

  async getInt(key: string, fallback = 0): Promise<number> {
    const v = await AppDNA.remoteConfig.get(key);
    if (typeof v === 'number') return Math.trunc(v);
    return fallback;
  },

  async getDouble(key: string, fallback = 0.0): Promise<number> {
    const v = await AppDNA.remoteConfig.get(key);
    return typeof v === 'number' ? v : fallback;
  },

  async getBool(key: string, fallback = false): Promise<boolean> {
    const v = await AppDNA.remoteConfig.get(key);
    return typeof v === 'boolean' ? v : fallback;
  },

  async getJson(key: string): Promise<Record<string, unknown> | null> {
    const v = await AppDNA.remoteConfig.get(key);
    return typeof v === 'object' && v !== null ? (v as Record<string, unknown>) : null;
  },
};
```

Call them like this:

```typescript theme={null}
declare const TypedRemoteConfig: {
  getString(key: string, fallback: string): Promise<string>;
  getInt(key: string, fallback: number): Promise<number>;
};

const welcome = await TypedRemoteConfig.getString('welcome_message', 'Hello!');
const retries = await TypedRemoteConfig.getInt('max_retries', 3);
```

## When Config Refreshes

The SDK refreshes remote config automatically:

* **On app launch** -- if the cached config has expired (default TTL: 1 hour)
* **On return from background** -- same TTL check

If the fetch fails, the SDK continues using cached values. On first launch with no connectivity, bundled config defaults are used.

<Info>
  The config TTL is configurable via `AppDNAOptions` `{ configTTL: 7200 }` (in seconds). The default is 3600 seconds (1 hour).
</Info>

## Listen for Config Changes

`onChanged` fires once per refresh whenever the SDK receives updated config from the server. Re-read the keys you care about inside the callback. Returns an unsubscribe function.

```typescript theme={null}
declare function updatePromoBanner(show: boolean): void;

// The callback receives NO arguments — re-read the keys you care about inside it.
const unsubscribe = AppDNA.remoteConfig.onChanged(async () => {
  const show = ((await AppDNA.remoteConfig.get('show_promo')) as boolean | undefined) ?? false;
  updatePromoBanner(show);
});
```

### Synchronous reads on the hot path

A per-render `await AppDNA.remoteConfig.get(...)` pays a bridge hop every render. Prime a snapshot
once after `configure()` and read it synchronously afterwards:

```typescript theme={null}
await AppDNA.remoteConfig.primeSnapshot();

if (AppDNA.remoteConfig.hasSnapshot()) {
  const banner = AppDNA.remoteConfig.getCached('home_banner_text'); // no bridge hop
  console.log(banner);
}
```

The snapshot refreshes itself whenever native applies new config. It is a read-through cache over
native, not a source of truth — `getCached` returns `undefined` before `primeSnapshot()` has run.

## Full Example

```tsx theme={null}
import React, { useEffect, useState } from 'react';
import { View, Text } from 'react-native';
import { AppDNA } from '@appdna-ai/react-native-sdk';

export function HomeScreen() {
  const [title, setTitle] = useState('Welcome');
  const [showBanner, setShowBanner] = useState(false);
  const [bannerText, setBannerText] = useState('');

  useEffect(() => {
    loadConfig();
    const unsubscribe = AppDNA.remoteConfig.onChanged(loadConfig);
    return unsubscribe;
  }, []);

  async function loadConfig() {
    const t = ((await AppDNA.remoteConfig.get('home_title')) as string | undefined) ?? 'Welcome';
    const sb = ((await AppDNA.remoteConfig.get('show_promo_banner')) as boolean | undefined) ?? false;
    const bt = ((await AppDNA.remoteConfig.get('promo_banner_text')) as string | undefined) ?? '';

    setTitle(t);
    setShowBanner(sb);
    setBannerText(bt);
  }

  return (
    <View>
      <Text style={{ fontSize: 24, fontWeight: 'bold' }}>{title}</Text>
      {showBanner ? <Text>{bannerText}</Text> : null}
      {/* Rest of the home screen */}
    </View>
  );
}
```

<Note>
  Remote config values are managed in the Console under **Settings > Remote Config**. Changes take effect on the next SDK config refresh.
</Note>
