Skip to content

Getting Started with de.sdk-rn

React Native SDK wrapper around de.eui (Map Service Interface) using WebView.

What You'll Learn

This guide covers:

  • Installing de.sdk-rn in your React Native project
  • Understanding the WebView architecture
  • Setting up authentication
  • Rendering the MSI component
  • Accessing controls and handles

Time to complete: 20 minutes

Architecture

de.sdk-rn wraps the web-based de.eui map interface in a React Native WebView component. Communication between React Native and the web map happens through webview.io bridge.

Key Components:

  • MSI - React component (renders WebView)
  • Auth - Authentication class
  • DClient - Order management (Order, Event, Client)
  • Utils - Utilities including Stream

Prerequisites

Before starting, ensure you have:

  • Node.js 16+ installed
  • React Native 0.68+ project set up
  • iOS Development: Xcode 13+, CocoaPods
  • Android Development: Android Studio, JDK 11+
  • De. Platform: Workspace ID and Access Token

Installation

Step 1: Install Package

1

Add SDK Package

Install via npm or yarn

bash
# Using npm
npm install @de./sdk-rn

# Using yarn
yarn add @de./sdk-rn

Step 2: iOS Setup

1

Install iOS Dependencies

Install CocoaPods

bash
cd ios
pod install
cd ..
2

Configure Info.plist

Add location permissions

Open ios/YourApp/Info.plist and add:

xml
<dict>
  <!-- Location Permissions -->
  <key>NSLocationWhenInUseUsageDescription</key>
  <string>We need your location to show delivery tracking</string>
  
  <key>NSLocationAlwaysAndWhenInUseUsageDescription</key>
  <string>We need your location for background delivery tracking</string>
  
  <key>NSLocationAlwaysUsageDescription</key>
  <string>We need your location for continuous tracking</string>
  
  <!-- Background Modes -->
  <key>UIBackgroundModes</key>
  <array>
    <string>location</string>
  </array>
</dict>

Step 3: Android Setup

1

Update AndroidManifest.xml

Add permissions

Open android/app/src/main/AndroidManifest.xml:

xml
<manifest>
  <!-- Location Permissions -->
  <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
  <uses-permission android:name="android.permission.ACCESS_BACKGROUND_LOCATION" />
  
  <!-- Internet -->
  <uses-permission android:name="android.permission.INTERNET" />
  
  <application>
    <!-- Your app config -->
  </application>
</manifest>
2

Update build.gradle

Minimum SDK version

Open android/app/build.gradle:

gradle
android {
    compileSdkVersion 33
    
    defaultConfig {
        applicationId "com.yourapp"
        minSdkVersion 23  // Minimum Android 6.0
        targetSdkVersion 33
    }
}

Environment Configuration

1

Install dotenv

For environment variables

bash
npm install react-native-dotenv
2

Create .env file

Store credentials securely

bash
# .env
DE_WORKSPACE_ID=your-workspace-id
DE_ACCESS_TOKEN=your-access-token
DE_ENVIRONMENT=prod
3

Configure babel.config.js

Enable env variables

javascript
module.exports = {
  presets: ['module:metro-react-native-babel-preset'],
  plugins: [
    ['module:react-native-dotenv', {
      moduleName: '@env',
      path: '.env',
    }]
  ]
}
4

TypeScript Configuration

Add type definitions

Create types/env.d.ts:

typescript
declare module '@env' {
  export const DE_WORKSPACE_ID: string
  export const DE_ACCESS_TOKEN: string
  export const DE_ENVIRONMENT: string
}

Your First Map

Basic MSI Component

1

Render MSI Component

App.tsx

typescript
import React, { useRef } from 'react'
import { View, StyleSheet } from 'react-native'
import { MSI, Auth, type MSIRef, type MSIInterface } from '@de./sdk-rn'
import { DE_CONTEXT, DE_CID, DE_SECRET, DE_REMOTE_ORIGIN } from '@env'

const auth = new Auth({
  context: DE_CONTEXT,
  cid: DE_CID,
  secret: DE_SECRET,
  remoteOrigin: DE_REMOTE_ORIGIN
}, {
  env: 'prod',
  autorefresh: true // Auto-refresh token before expiry
})

export default function App() {
  const msiRef = useRef<MSIRef>(null)
  
  const handleLoaded = ({ controls, handles }: MSIInterface) => {
    console.log('MSI loaded - ready to use!')
    // Access map controls and handles here
  }
  
  return (
    <View style={styles.container}>
      <MSI
        ref={msiRef}
        env="prod"
        getAccessToken={() => auth.accessToken || ''}
        onLoaded={handleLoaded}
        onError={(error) => console.error(error)}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 }
})

Using Controls API

2

Add Route

Use controls to create routes

typescript
import React, { useRef } from 'react'
import { View, StyleSheet } from 'react-native'
import { MSI, Auth, type MSIRef, type MSIInterface } from '@de./sdk-rn'

const auth = new Auth(credentials, { env: 'prod', autorefresh: true })

export default function DeliveryMap() {
  const msiRef = useRef<MSIRef>(null)
  
  const handleLoaded = async ({ controls }: MSIInterface) => {
    // Set route using controls
    await controls.setRoute({
      routeId: 'delivery-1',
      origin: {
        coords: { lng: -74.0060, lat: 40.7128 },
        caption: { label: 'Pickup' }
      },
      destination: {
        coords: { lng: -73.9855, lat: 40.7580 },
        caption: { label: 'Delivery' }
      },
      options: {
        profile: 'driving-traffic',
        animation: 'dot:flow'
      }
    })
    
    // Fit route in view
    await controls.fitRoutesBounds({ margin: 100 })
  }
  
  return (
    <View style={styles.container}>
      <MSI
        ref={msiRef}
        env="prod"
        getAccessToken={() => auth.accessToken || ''}
        onLoaded={handleLoaded}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 }
})

Requesting Permissions

Location Permissions

1

Request Permissions

utils/permissions.ts

typescript
import { Platform, PermissionsAndroid } from 'react-native'
import Geolocation from '@react-native-community/geolocation'

export async function requestLocationPermission(): Promise<boolean> {
  if (Platform.OS === 'ios') {
    return requestIOSPermission()
  } else {
    return requestAndroidPermission()
  }
}

async function requestIOSPermission(): Promise<boolean> {
  return new Promise((resolve) => {
    Geolocation.requestAuthorization()
    // iOS handles permission UI automatically
    resolve(true)
  })
}

async function requestAndroidPermission(): Promise<boolean> {
  try {
    const granted = await PermissionsAndroid.requestMultiple([
      PermissionsAndroid.PERMISSIONS.ACCESS_FINE_LOCATION,
      PermissionsAndroid.PERMISSIONS.ACCESS_COARSE_LOCATION,
    ])
    
    return (
      granted['android.permission.ACCESS_FINE_LOCATION'] === 
      PermissionsAndroid.RESULTS.GRANTED
    )
  } catch (err) {
    console.warn(err)
    return false
  }
}

export async function requestBackgroundLocationPermission(): Promise<boolean> {
  if (Platform.OS === 'android' && Platform.Version >= 29) {
    const granted = await PermissionsAndroid.request(
      PermissionsAndroid.PERMISSIONS.ACCESS_BACKGROUND_LOCATION
    )
    return granted === PermissionsAndroid.RESULTS.GRANTED
  }
  return true
}
2

Use Permissions

Request before using location

typescript
import React, { useEffect, useState } from 'react'
import { View, Text, Button } from 'react-native'
import { requestLocationPermission } from './utils/permissions'

export default function App() {
  const [hasPermission, setHasPermission] = useState(false)
  
  useEffect(() => {
    checkPermissions()
  }, [])
  
  async function checkPermissions() {
    const granted = await requestLocationPermission()
    setHasPermission(granted)
  }
  
  if (!hasPermission) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Location permission required</Text>
        <Button title="Grant Permission" onPress={checkPermissions} />
      </View>
    )
  }
  
  return <MapScreen />
}

Using Handles (Streams)

Track Peer Location

1

Stream-based Tracking

Real-time peer location updates

typescript
import React, { useRef } from 'react'
import { View, StyleSheet } from 'react-native'
import { MSI, Auth, Utils, type MSIInterface } from '@de./sdk-rn'

const auth = new Auth(credentials, { env: 'prod', autorefresh: true })

export default function DriverTracker() {
  const handleLoaded = ({ handles }: MSIInterface) => {
    // Initial driver location
    const driverLocation = {
      lng: -74.0060,
      lat: 40.7128,
      heading: 45
    }
    
    // Create peer location stream
    const peerStream = handles.peerLocation(driverLocation, {
      label: 'Driver',
      duration: 5,
      unit: 'min'
    })
    
    if (!peerStream) return
    
    // Create live stream for updates
    const liveStream = new Utils.Stream()
    
    // Simulate real-time updates (replace with WebSocket)
    const interval = setInterval(() => {
      driverLocation.lat += 0.0001
      driverLocation.lng -= 0.0001
      
      liveStream.sync({
        position: driverLocation,
        caption: { duration: 4, unit: 'min' }
      })
    }, 2000)
    
    // Pipe updates to peer stream
    liveStream.pipe(peerStream)
    
    // Cleanup
    setTimeout(() => {
      clearInterval(interval)
      liveStream.close()
    }, 60000)
  }
  
  return (
    <View style={styles.container}>
      <MSI
        env="prod"
        getAccessToken={() => auth.accessToken || ''}
        onLoaded={handleLoaded}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 }
})

DClient - Order Management

Working with Orders

1

Order Operations

Using DClient.Order

typescript
import { DClient, Auth } from '@de./sdk-rn'

const auth = new Auth(credentials, { env: 'prod', autorefresh: true })

// Initialize Order client
const orderClient = new DClient.Order({
  context: credentials.context,
  getAccessToken: () => auth.accessToken || ''
})

async function manageOrder() {
  try {
    // Generate intent token for order
    const intentToken = await orderClient.intent('client-id-123')
    
    // Add waypoints
    const waypoints = await orderClient.addWaypoint([
      {
        no: 1,
        type: 'pickup',
        description: 'Restaurant',
        coordinates: { lng: -74.0060, lat: 40.7128 },
        contact: {
          type: 'business',
          reference: 'restaurant-1'
        }
      },
      {
        no: 2,
        type: 'dropoff',
        description: 'Customer',
        coordinates: { lng: -73.9855, lat: 40.7580 },
        contact: {
          type: 'client',
          reference: 'client-123',
          phone: '+1234567890'
        }
      }
    ], intentToken)
    
    // Add packages
    await orderClient.addPackage({
      waypointNo: 1,
      careLevel: 2,
      category: 'food',
      weight: 2.5
    }, intentToken)
    
    // Update order stage
    await orderClient.updateStage('assigned', intentToken)
    
    return { intentToken, waypoints }
  } catch (error) {
    console.error('Order error:', error)
    throw error
  }
}

Complete Example

Delivery Tracking App

typescript
// App.tsx
import React, { useEffect, useState } from 'react'
import { NavigationContainer } from '@react-navigation/native'
import { createStackNavigator } from '@react-navigation/stack'
import { requestLocationPermission } from './utils/permissions'

import HomeScreen from './screens/HomeScreen'
import TrackingScreen from './screens/TrackingScreen'
import OrderScreen from './screens/OrderScreen'

const Stack = createStackNavigator()

export default function App() {
  const [ready, setReady] = useState(false)
  
  useEffect(() => {
    async function initialize() {
      await requestLocationPermission()
      setReady(true)
    }
    
    initialize()
  }, [])
  
  if (!ready) {
    return null // Or loading screen
  }
  
  return (
    <NavigationContainer>
      <Stack.Navigator>
        <Stack.Screen 
          name="Home" 
          component={HomeScreen}
          options={{ title: 'My Deliveries' }}
        />
        <Stack.Screen 
          name="Tracking" 
          component={TrackingScreen}
          options={{ title: 'Track Delivery' }}
        />
        <Stack.Screen 
          name="Order" 
          component={OrderScreen}
          options={{ title: 'New Order' }}
        />
      </Stack.Navigator>
    </NavigationContainer>
  )
}

Troubleshooting

iOS Build Fails

Error: Pod install failed

Solution:

bash
cd ios
rm -rf Pods Podfile.lock
pod deintegrate
pod install
cd ..

Android Build Errors

Error: Manifest merger failed

Solution: Check AndroidManifest.xml for duplicate permissions. Ensure minSdkVersion is 23 or higher.

Location Not Working

Error: Location always returns null

Solution:

  • Check permissions are granted
  • Enable location services on device
  • For iOS simulator: Features → Location → Custom Location
  • For Android emulator: Extended controls → Location

Map Not Displaying

Error: Blank map view

Solution:

  • Verify access token is correct
  • Check internet connection
  • Clear Metro bundler cache:
bash
npm start -- --reset-cache

Best Practices

Do

  • Request permissions before using location
  • Handle permission denial gracefully
  • Clean up WebSocket connections
  • Use environment variables for secrets
  • Test on real devices
  • Implement offline fallbacks

Avoid

  • Hardcoding access tokens
  • Tracking location continuously when not needed
  • Ignoring battery optimization
  • Forgetting to unsubscribe from events
  • Testing only on simulators
  • Storing sensitive data in AsyncStorage

Next Steps

Additional Resources

API Reference

Complete React Native SDK API documentation

Learn more →

Examples

Sample apps and code snippets

Learn more →

Troubleshooting

Common issues and solutions

Learn more →