This codelab demonstrates how to implement the RELID Initialization flow using the react-native-rdna-client npm plugin. The RELID SDK provides secure identity verification and session management for mobile applications.

What You'll Learn

What You'll Need

Get the Code from GitHub

The code to get started is stored in a GitHub repository.

You can clone the repository using the following command:

git clone https://github.com/uniken-public/codelab-react-native.git

Navigate to the relid-initialize folder in the repository you cloned earlier

Before implementing your own RELID initialization, let's examine the sample app structure to understand the recommended architecture:

Component

Purpose

Sample App Reference

Connection Profile

Configuration data

src/uniken/cp/agent_info.json

Profile Parser

Utility to parse connection data

src/uniken/utils/connectionProfileParser.ts

Event Manager

Handles SDK callbacks

src/uniken/services/rdnaEventManager.ts

RELID Service

Main SDK interface

src/uniken/services/rdnaService.ts

Event Provider

Global event handling

src/uniken/providers/SDKEventProvider.tsx

UI Components

User interface screens

src/tutorial/screens/tutorial/TutorialHomeScreen.tsx

Recommended Directory Structure

Create the following directory structure in your React Native project:

src/
├── uniken/
│   ├── cp/
│   │   └── agent_info.json
│   ├── services/
│   │   ├── rdnaEventManager.ts
│   │   └── rdnaService.ts
│   ├── utils/
│   │   └── connectionProfileParser.ts
│   ├── types/
│   │   └── rdnaEvents.ts
│   ├── providers/
│   │   └── SDKEventProvider.tsx
│   └── MTDContext/
│       └── MTDThreatContext.tsx
├── tutorial/
│   ├── navigation/
│   │   ├── AppNavigator.tsx
│   │   └── NavigationService.ts
│   └── screens/
│       ├── index.ts
│       └── tutorial/
│           ├── SecurityExitScreen.tsx
│           ├── TutorialHomeScreen.tsx
│           ├── TutorialErrorScreen.tsx
│           └── TutorialSuccessScreen.tsx
└── assets/

Add the RELID Plugin

Add the react-native-rdna-client plugin to your project:

npm install react-native-rdna-client

Platform Setup

Follow the platform-specific setup instructions provided with the plugin for both iOS and Android configurations.

agent_info.json

Create your connection profile JSON file in src/uniken/cp/agent_info.json:

{
  "RelIds": [
    {
      "Name": "YourRELIDAgentName",
      "RelId": "your-rel-id-string-here"
    }
  ],
  "Profiles": [
    {
      "Name": "YourRELIDAgentName",
      "Host": "your-gateway-host.com",
      "Port": "443"
    }
  ]
}

Security Considerations

Define the TypeScript interfaces for type safety:

// src/uniken/utils/connectionProfileParser.ts
interface RelId {
  Name: string;
  RelId: string;
}

interface Profile {
  Name: string;
  Host: string;
  Port: string | number;
}

interface AgentInfo {
  RelIds: RelId[];
  Profiles: Profile[];
}

interface ParsedAgentInfo {
  relId: string;
  host: string;
  port: number;
}

export const parseAgentInfo = (profileData: AgentInfo): ParsedAgentInfo => {
  if (!profileData.RelIds || profileData.RelIds.length === 0) {
    throw new Error('No RelIds found in agent info');
  }

  if (!profileData.Profiles || profileData.Profiles.length === 0) {
    throw new Error('No Profiles found in agent info');
  }

  // Always pick the first array objects
  const firstRelId = profileData.RelIds[0];

  if (!firstRelId.Name || !firstRelId.RelId) {
    throw new Error('Invalid RelId object - missing Name or RelId');
  }

  // Find matching profile by Name
  const matchingProfile = profileData.Profiles.find(
    profile => profile.Name === firstRelId.Name
  );

  if (!matchingProfile) {
    throw new Error(`No matching profile found for RelId name: ${firstRelId.Name}`);
  }

  if (!matchingProfile.Host || !matchingProfile.Port) {
    throw new Error('Invalid Profile object - missing Host or Port');
  }

  // Convert port to number if it's a string
  const port = typeof matchingProfile.Port === 'string'
    ? parseInt(matchingProfile.Port, 10)
    : matchingProfile.Port;

  if (isNaN(port)) {
    throw new Error(`Invalid port value: ${matchingProfile.Port}`);
  }

  return {
    relId: firstRelId.RelId,
    host: matchingProfile.Host,
    port: port
  };
};

export const loadAgentInfo = async (): Promise<ParsedAgentInfo> => {
  try {
    const profileData = require('../cp/agent_info.json');
    return parseAgentInfo(profileData);
  } catch (error) {
    throw new Error(`Failed to load agent info: ${error instanceof Error ? error.message : String(error)}`);
  }
};

The parser performs several critical functions:

The event manager handles all RELID SDK callbacks using a singleton pattern:

// src/uniken/services/rdnaEventManager.ts
import { NativeEventEmitter, NativeModules } from 'react-native';
import type {
  RDNAJsonResponse,
  RDNAProgressData,
  RDNAInitializeErrorData,
  RDNAInitializedData,
  RDNAInitializeProgressCallback,
  RDNAInitializeErrorCallback,
  RDNAInitializeSuccessCallback
} from '../types/rdnaEvents';

class RdnaEventManager {
  private static instance: RdnaEventManager;
  private rdnaEmitter: NativeEventEmitter;
  private listeners: Array<any> = [];

    // Composite event handlers (can handle multiple concerns)
  private initializeProgressHandler?: RDNAInitializeProgressCallback;
  private initializeErrorHandler?: RDNAInitializeErrorCallback;
  private initializedHandler?: RDNAInitializeSuccessCallback;

  constructor() {
    this.rdnaEmitter = new NativeEventEmitter(NativeModules.RdnaClient);
    this.registerEventListeners();
  }

  static getInstance(): RdnaEventManager {
    if (!RdnaEventManager.instance) {
      RdnaEventManager.instance = new RdnaEventManager();
    }
    return RdnaEventManager.instance;
  }

  private registerEventListeners() {
    console.log('RdnaEventManager - Registering native event listeners');

    this.listeners.push(
      this.rdnaEmitter.addListener('onInitializeProgress', this.onInitializeProgress.bind(this)),
      this.rdnaEmitter.addListener('onInitializeError', this.onInitializeError.bind(this)),
      this.rdnaEmitter.addListener('onInitialized', this.onInitialized.bind(this)),
      this.rdnaEmitter.addListener('onSdkLogPrintRequest', this.onSdkLogPrintRequest.bind(this)),
    );

    console.log('RdnaEventManager - Native event listeners registered');
  }
}

Event Processing

Each event handler performs JSON parsing and calls appropriate callbacks:

/**
 * Handles SDK initialization progress events
 */
private onInitializeProgress(response: RDNAJsonResponse) {
  console.log("RdnaEventManager - Initialize progress event received");

  try {
    const progressData: RDNAProgressData = JSON.parse(response.response);
    console.log("RdnaEventManager - Progress:", progressData.initializeStatus);

    if (this.initializeProgressHandler) {
      this.initializeProgressHandler(progressData);
    }
  } catch (error) {
    console.error("RdnaEventManager - Failed to parse initialize progress:", error);
  }
}

/**
 * Handles SDK initialization error events
 */
private onInitializeError(response: RDNAJsonResponse) {
  console.log("RdnaEventManager - Initialize error event received");

  try {
    const errorData: RDNAInitializeErrorData = JSON.parse(response.response);
    console.error("RdnaEventManager - Initialize error:", errorData.errorString);
    
    if (this.initializeErrorHandler) {
      this.initializeErrorHandler(errorData);
    }
  } catch (error) {
    console.error("RdnaEventManager - Failed to parse initialize error:", error);
  }
}

/**
 * Handles SDK initialization success events
 */
private onInitialized(response: RDNAJsonResponse) {
  console.log("RdnaEventManager - Initialize success event received");

  try {
    const initializedData: RDNAInitializedData = JSON.parse(response.response);
    console.log("RdnaEventManager - Successfully initialized, Session ID:", initializedData.session.sessionID);

    if (this.initializedHandler) {
      this.initializedHandler(initializedData);
    }
  } catch (error) {
    console.error("RdnaEventManager - Failed to parse initialize success:", error);
  }
}

/**
 * Sets event handlers for SDK events. Only one handler per event type.
 */
public setInitializeProgressHandler(callback?: RDNAInitializeProgressCallback): void {
  this.initializeProgressHandler = callback;
}

public setInitializeErrorHandler(callback?: RDNAInitializeErrorCallback): void {
  this.initializeErrorHandler = callback;
}

public setInitializedHandler(callback?: RDNAInitializeSuccessCallback): void {
  this.initializedHandler = callback;
}

/**
 * Cleans up all event listeners and handlers
 */
public cleanup() {
  console.log('RdnaEventManager - Cleaning up event listeners and handlers');
  
  // Remove native event listeners
  this.listeners.forEach(listener => {
    if (listener && listener.remove) {
      listener.remove();
    }
  });
  this.listeners = [];
  
  // Clear all event handlers
  this.initializeProgressHandler = undefined;
  this.initializeErrorHandler = undefined;
  this.initializedHandler = undefined;
  console.log('RdnaEventManager - Cleanup completed');
}

export default RdnaEventManager;

Key features of the event manager:

The RELID service provides the main interface for SDK operations:

// src/uniken/services/rdnaService.ts
import RdnaClient, {
  RDNALoggingLevel,
} from 'react-native-rdna-client/src/index';
import { loadAgentInfo } from '../utils/connectionProfileParser';
import RdnaEventManager from './rdnaEventManager';
import type {
  RDNASyncResponse,
  InitOptions,
} from '../types/rdnaEvents';

export class RdnaService {
  private static instance: RdnaService;
  private eventManager: RdnaEventManager;

  constructor() {
    this.eventManager = RdnaEventManager.getInstance();
  }

  static getInstance(): RdnaService {
    if (!RdnaService.instance) {
      RdnaService.instance = new RdnaService();
    }
    return RdnaService.instance;
  }

    /**
   * Cleans up the service and event manager
   */
  cleanup(): void {
    console.log('RdnaService - Cleaning up service');
    this.eventManager.cleanup();
  }

  /**
   * Gets the event manager instance for external callback setup
   */
  getEventManager(): RdnaEventManager {
    return this.eventManager;
  }

  /**
   * Initializes the REL-ID SDK with optional advanced configuration
   * @param initOptions - Optional configuration for language, permissions, and telemetry
   * @returns Promise<RDNASyncResponse> that resolves with sync response structure
   */
  async initialize(initOptions?: InitOptions): Promise<RDNASyncResponse> {

    const profile = await loadAgentInfo();
    console.log('RdnaService - Loaded connection profile:', {
      host: profile.host,
      port: profile.port,
      relId: profile.relId.substring(0, 10) + '...',
    });

    // Use provided options or default values
    const defaultInitOptions: InitOptions = {
      internationalizationOptions: {
        localeCode: '',           // Empty string - SDK will default to 'en'
        localeName: '',
        languageDirection: 0      // 0 = LTR (Left-to-Right)
      },
      permissionOptions: {
        isLocationPermissionRequired: true,
        isLocationPermissionMandatory: false
      },
      otelConfig: {
        otelHTTPEndpointURL: '',
        enableEncoding: '',
        disableTrace: 1,          // Tracing disabled by default
        otelTraceFlushTimeout: 0
      }
    };

    const finalInitOptions = initOptions || defaultInitOptions;
    const initOptionsString = JSON.stringify(finalInitOptions);

    console.log('RdnaService - Starting initialization');
    console.log('RdnaService - InitOptions:', initOptionsString);

    return new Promise((resolve, reject) => {
      RdnaClient.initialize(
        profile.relId,                   // agentInfo: RelId from connection profile
        profile.host,                    // gatewayHost: Server hostname from connection profile
        profile.port,                    // gatewayPort: Server port from connection profile
        '',                              // cipherSpec: Empty for default
        '',                              // cipherSalt: Empty for default
        '',                              // proxySettings: Empty for no proxy
        '',                              // dnsServerList: Empty for default
        RDNALoggingLevel.RDNA_NO_LOGS,   // logLevel: No logs for production
        initOptionsString,                // initOptions: Advanced SDK configuration (JSON string)
        response => {
          console.log('RdnaService - Initialize sync callback received');
          console.log('RdnaService - initialize Sync raw response', response);

          const result: RDNASyncResponse = response;
          
          if (result.error && result.error.longErrorCode === 0) {
            console.log(
              'RdnaService - Sync response success, waiting for async events',
            );
            resolve(result);
          } else {
            console.error('RdnaService - Sync response error:', result);
            reject(result);
          }
        },
      );
    });
  }

  /**
   * Gets the version of the REL-ID SDK
   */
  async getSDKVersion(): Promise<string> {
    return new Promise((resolve, reject) => {
      RdnaClient.getSDKVersion(response => {
        console.log('RdnaService - SDK version response received');

        try {
          const version = response?.response || 'Unknown';
          console.log('RdnaService - SDK Version:', version);
          resolve(version);
        } catch (error) {
          console.error('RdnaService - Failed to parse SDK version:', error);
          reject(new Error('Failed to parse SDK version response'));
        }
      });
    });
  }
}

export default RdnaService.getInstance();

Important API Parameters

The RdnaClient.initialize() call requires specific parameter ordering:

Parameter

Purpose

Example

agentInfo

RelId

From connection profile Ex. {"Name": "YourRELIDAgentName","RelId": "your-rel-id-string-here"}

gatewayHost

Server hostname

From connection profile Ex. {"Host": "your-gateway-host.com","Port": "443"}

gatewayPort

Server port

From connection profile Ex. {"Host": "your-gateway-host.com","Port": "443"}

logLevel

Logging setting

RDNA_NO_LOGS for production

initOptions

SDK configuration (optional)

JSON string with language, permissions, and telemetry settings

The RELID SDK supports advanced configuration through the initOptions parameter. This allows you to customize language settings, permission requirements, and telemetry options based on your application's needs.

InitOptions Interface

The InitOptions interface is defined in src/uniken/types/rdnaEvents.ts:

export interface InitOptions {
  internationalizationOptions: {
    localeCode: string;         // ISO 639-1 language code (e.g., 'en', 'ar', 'es')
    localeName: string;          // Display name for the locale
    languageDirection: number;   // 0 = LTR (Left-to-Right), 1 = RTL (Right-to-Left)
  };
  permissionOptions: {
    isLocationPermissionRequired: boolean;   // Whether location permission is needed
    isLocationPermissionMandatory: boolean;  // If true, SDK fails without permission
  };
  otelConfig: {
    otelHTTPEndpointURL: string;      // OpenTelemetry HTTP endpoint URL
    enableEncoding: string;            // Encoding format for OTel data
    disableTrace: number;              // 0 = enable tracing, 1 = disable tracing
    otelTraceFlushTimeout: number;     // Timeout in milliseconds for flushing traces
  };
}

Language Options Configuration

Language options control the SDK's display language and text direction (LTR/RTL):

// Example: Arabic (RTL) configuration
const language = 'ar';  // Get from i18n library or user settings
const languageDirection = ['ar', 'he', 'fa', 'ur'].includes(language) ? 1 : 0;

const initOptions: InitOptions = {
  internationalizationOptions: {
    localeCode: language,        // ISO 639-1 code: 'ar' for Arabic
    localeName: 'Arabic',         // Display name
    languageDirection: 1          // RTL direction
  },
  // ... other options
};

RTL Languages: Arabic (ar), Hebrew (he), Persian (fa), Urdu (ur) LTR Languages: All others (default)

Common Use Cases:

Permission Options Configuration

Permission options define whether the SDK requires location access and if it's mandatory:

const initOptions: InitOptions = {
  // ... language options
  permissionOptions: {
    isLocationPermissionRequired: true,   // Request location permission
    isLocationPermissionMandatory: false  // SDK works without it (limited functionality)
  },
  // ... otel config
};

Configuration Scenarios:

Scenario

Required

Mandatory

Behavior

Location-based features

true

false

SDK requests permission but works without it

Location required for compliance

true

true

SDK initialization fails without permission

No location needed

false

false

SDK doesn't request location permission

Use Cases:

OpenTelemetry (OTel) Configuration

OpenTelemetry configuration enables enterprise-level monitoring and distributed tracing:

const initOptions: InitOptions = {
  // ... language options
  // ... permission options
  otelConfig: {
    otelHTTPEndpointURL: 'https://your-otel-collector.example.com',  // Your OTel endpoint
    enableEncoding: '',           // Encoding format (empty for default)
    disableTrace: 0,              // 0 = enabled, 1 = disabled
    otelTraceFlushTimeout: 0      // Flush timeout in milliseconds (0 = default)
  }
};

Default Configuration (Recommended for Most Apps):

otelConfig: {
  otelHTTPEndpointURL: '',  // Empty = telemetry disabled
  enableEncoding: '',
  disableTrace: 1,          // Tracing disabled
  otelTraceFlushTimeout: 0
}

Use Cases:

Complete Configuration Example

Here's a complete example showing all three configuration areas:

// Example: Arabic banking app with location and telemetry
const initOptions: InitOptions = {
  internationalizationOptions: {
    localeCode: 'ar',
    localeName: 'Arabic',
    languageDirection: 1  // RTL
  },
  permissionOptions: {
    isLocationPermissionRequired: true,
    isLocationPermissionMandatory: false
  },
  otelConfig: {
    otelHTTPEndpointURL: 'https://telemetry.yourbank.com',
    enableEncoding: '',
    disableTrace: 0,  // Enabled for enterprise monitoring
    otelTraceFlushTimeout: 5000
  }
};

// Call initialize with custom configuration
await rdnaService.initialize(initOptions);

Backward Compatibility

The initOptions parameter is optional. Existing code without initOptions continues to work with default values:

// Works with default configuration
await rdnaService.initialize();

// Works with custom configuration
await rdnaService.initialize(initOptions);

Create a screen component that handles user interaction and displays progress:

// src/tutorial/screens/tutorial/TutorialHomeScreen.tsx
import React, { useState, useEffect } from 'react';
import {
  View,
  Text,
  TouchableOpacity,
  StyleSheet,
  StatusBar,
  ScrollView,
  ActivityIndicator,
  Alert,
} from 'react-native';
import { useNavigation } from '@react-navigation/native';
import type { NativeStackNavigationProp } from '@react-navigation/native-stack';
import type { RootStackParamList } from '../../navigation/AppNavigator';
import rdnaService from '../../../uniken/services/rdnaService';
import { getProgressMessage } from '../../../uniken/utils/progressHelper';
import type {
  RDNAProgressData,
  RDNASyncResponse,
  RDNAError,
  RDNAInitializeErrorData,
  InitOptions,
} from '../../../uniken/types/rdnaEvents';

type TutorialHomeNavigationProp = NativeStackNavigationProp<RootStackParamList, 'TutorialHome'>;

const TutorialHomeScreen: React.FC = () => {
  const navigation = useNavigation<TutorialHomeNavigationProp>();
  const [sdkVersion, setSdkVersion] = useState<string>('Loading...');
  const [isInitializing, setIsInitializing] = useState<boolean>(false);
  const [progressMessage, setProgressMessage] = useState<string>('');
  const [initializeError, setInitializeError] = useState<RDNAInitializeErrorData | null>(null);

  useEffect(() => {
    loadSDKVersion();

    // Register error handler directly in TutorialHomeScreen
    const eventManager = rdnaService.getEventManager();
    eventManager.setInitializeErrorHandler((errorData: RDNAInitializeErrorData) => {
      console.log('TutorialHomeScreen - Received initialize error:', errorData);
      
      // Update local state
      setIsInitializing(false);
      setProgressMessage('');
      setInitializeError(errorData);
      
      // Navigate to error screen with the error details
      navigation.navigate('TutorialError', {
        shortErrorCode: errorData.shortErrorCode,
        longErrorCode: errorData.longErrorCode,
        errorString: errorData.errorString,
      });
    });

    return () => {
      // Cleanup - reset handlers
      const eventManager = rdnaService.getEventManager();
      eventManager.setInitializeProgressHandler(undefined);
      eventManager.setInitializeErrorHandler(undefined);
      //rdnaService.cleanup();
      setIsInitializing(false);
      setProgressMessage('');
      setInitializeError(null);
    };
  }, [navigation]);

  const loadSDKVersion = async () => {
    try {
      const version = await rdnaService.getSDKVersion();
      setSdkVersion(version);
    } catch (error) {
      console.error('Failed to load SDK version:', error);
      setSdkVersion('Unknown');
    }
  };

  const handleInitializePress = () => {
    if (isInitializing) return;

    setIsInitializing(true);
    setProgressMessage('Starting RDNA initialization...');

    console.log('TutorialHomeScreen - User clicked Initialize - Starting RDNA...');

    // ========================================
    // CONFIGURE SDK INITIALIZATION OPTIONS
    // ========================================
    // Customers: Customize this section based on your application requirements

    // Determine language and direction
    const language = 'en';  // Get from your i18n library (e.g., i18next.language)
    const languageDirection = ['ar', 'he', 'fa', 'ur'].includes(language) ? 1 : 0;

    // Configure permissions
    const requireLocationPermission = true;   // Does your app need location?
    const locationIsMandatory = false;        // Can SDK work without it?

    // Configure telemetry
    const enableTelemetry = false;  // Set to true for enterprise monitoring

    // Build the initOptions configuration object
    const initOptions: InitOptions = {
      internationalizationOptions: {
        localeCode: language,
        localeName: language,
        languageDirection: languageDirection
      },
      permissionOptions: {
        isLocationPermissionRequired: requireLocationPermission,
        isLocationPermissionMandatory: locationIsMandatory
      },
      otelConfig: {
        otelHTTPEndpointURL: enableTelemetry ? 'https://your-otel-collector.example.com' : '',
        enableEncoding: '',
        disableTrace: enableTelemetry ? 0 : 1,
        otelTraceFlushTimeout: 0
      }
    };

    console.log('TutorialHomeScreen - Initializing with custom options:', initOptions);

    // Register progress handler directly with the event manager
    const eventManager = rdnaService.getEventManager();
    eventManager.setInitializeProgressHandler((data: RDNAProgressData) => {
      console.log('TutorialHomeScreen - Progress update:', data);
      const message = getProgressMessage(data);
      setProgressMessage(message);
    });

    // Call rdnaService.initialize() with custom configuration
    rdnaService.initialize(initOptions)
      .then((syncResponse: RDNASyncResponse) => {
        console.log('TutorialHomeScreen - RDNA initialization promise resolved successfully');
        console.log('TutorialHomeScreen - Sync response received:', {
          longErrorCode: syncResponse.error?.longErrorCode,
          shortErrorCode: syncResponse.error?.shortErrorCode,
          errorString: syncResponse.error?.errorString
        });
        
        // Success will be handled by the SDKEventProvider's onInitialized handler
        // which automatically navigates to the success screen
      })
      .catch((error) => {
        console.error('TutorialHomeScreen - RDNA initialization promise rejected:', error);
        setIsInitializing(false);
        setProgressMessage('');
        
        const result: RDNASyncResponse = error;
       /*
        Error Code: 88 (RDNA_ERR_RDNA_ALREADY_INITIALIZED)
        Terminate the SDK to avoid re-initialization. This helps during development and prevents errors during React Native code refresh.
        Error Code: 179 (RDNA_ERR_INITIALIZE_ALREADY_IN_PROGRESS)
        Avoid invoking the Initialize API again while initialization is already in progress. Wait for the current initialization to complete.
        Error Code: 218 (RDNA_ERR_DEVICE_SECURITY_CHECKS_FAILED_FRIDA_MODULES_DETECTED)
        The SDK detected a Frida attack.
        */
        
        Alert.alert(
          'Initialization Failed',
          `${result.error.errorString}\n\nError Codes:\nLong: ${result.error.longErrorCode}\nShort: ${result.error.shortErrorCode}`,
          [
            { text: 'OK', style: 'default' }
          ]
        );
      });
  };

  // Component JSX would include SDK version display, initialize button, and progress display
};

Success Event Handling

The sample app uses a centralized approach for handling the onInitialized success event through SDKEventProvider.tsx:

// src/uniken/providers/SDKEventProvider.tsx
export const SDKEventProvider: React.FC<SDKEventProviderProps> = ({ children }) => {
  useEffect(() => {
    const eventManager = rdnaService.getEventManager();
    // Set up event handlers once on mount
    eventManager.setInitializedHandler(handleInitialized);
    return () => {
      console.log('SDKEventProvider - Component unmounting, cleaning up event handlers');
      eventManager.cleanup();
    };
  }, []);

  /**
   * Event handler for successful initialization
   */
  const handleInitialized = useCallback((data: RDNAInitializedData) => {
    console.log('SDKEventProvider - Successfully initialized, Session ID:', data.session.sessionID);
    
    NavigationService.navigate('TutorialSuccess', {
      statusCode: data.status.statusCode,
      statusMessage: data.status.statusMessage,
      sessionId: data.session.sessionID,
      sessionType: data.session.sessionType,
    });
  }, []);

  return (
    <SDKEventContext.Provider value={contextValue}>
      {children}
    </SDKEventContext.Provider>
  );
};

This provider should wrap your app's root navigation to ensure the onInitialized event is handled globally.

Key Implementation Features

The sample app demonstrates several important patterns:

Promise-based API Usage:

Error Handling:

Progress Tracking:

Event-Driven Architecture:

The following images showcase screens from the sample application:

Initialize Progress Screen

Initialize Error Screen

Initialize Screen

Key Test Scenarios

  1. Successful Initialization: Verify the complete flow works end-to-end
  2. Network Errors: Test with invalid host/port configurations
  3. Invalid Credentials: Test with incorrect RelId values
  4. Progress Tracking: Verify progress events are properly displayed
  5. Default Configuration: Test initialization without initOptions parameter
  6. Custom Language: Test with RTL languages (Arabic, Hebrew, etc.)
  7. Location Permissions: Test with mandatory and non-mandatory location settings

Verification Steps

Test your implementation with below APIs:

// Test SDK version retrieval
rdnaService.getSDKVersion()
  .then((version) => console.log('SDK Version:', version))
  .catch((error) => console.error('Version failed:', error));

// Test basic initialization (with default values)
rdnaService.initialize()
  .then((syncResponse) => console.log('Initialization successful:', syncResponse))
  .catch((error) => console.error('Initialization failed:', error));

// Test initialization with custom initOptions
const customOptions = {
  internationalizationOptions: {
    localeCode: 'ar',
    localeName: 'Arabic',
    languageDirection: 1
  },
  permissionOptions: {
    isLocationPermissionRequired: true,
    isLocationPermissionMandatory: false
  },
  otelConfig: {
    otelHTTPEndpointURL: '',
    enableEncoding: '',
    disableTrace: 1,
    otelTraceFlushTimeout: 0
  }
};

rdnaService.initialize(customOptions)
  .then((syncResponse) => console.log('Custom initialization successful:', syncResponse))
  .catch((error) => console.error('Custom initialization failed:', error));

The sample app includes comprehensive testing patterns in the tutorial screens:

Connection Profile Issues

Error: "No RelIds found in agent info" Solution: Verify your JSON structure matches the sample in src/uniken/cp/agent_info.json

Error: "No matching profile found for RelId name" Solution: Ensure the Name field in RelIds matches the Name field in Profiles

Network Connectivity

Error: Network connection failures Solution: Check host/port values and network accessibility

Error: REL-ID connection issues Solution: Verify the REL-ID server setup and running

SDK Initialization

Error Code 88: SDK already initialized Solution: Terminate the sdk

Error Code 288: SDK detected dynamic attack performed on the app Solution: Terminate the app

Error Code 179: Initialization in progress Solution: Wait for current initialization to complete before retrying

Security Considerations

Memory Management

User Experience

Congratulations! You've successfully learned how to implement RELID SDK initialization with:

Next Steps