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 classDClient- 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
Add SDK Package
Install via npm or yarn
# Using npm
npm install @de./sdk-rn
# Using yarn
yarn add @de./sdk-rnStep 2: iOS Setup
Install iOS Dependencies
Install CocoaPods
cd ios
pod install
cd ..Configure Info.plist
Add location permissions
Open ios/YourApp/Info.plist and add:
<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
Update AndroidManifest.xml
Add permissions
Open android/app/src/main/AndroidManifest.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>Update build.gradle
Minimum SDK version
Open android/app/build.gradle:
android {
compileSdkVersion 33
defaultConfig {
applicationId "com.yourapp"
minSdkVersion 23 // Minimum Android 6.0
targetSdkVersion 33
}
}Environment Configuration
Install dotenv
For environment variables
npm install react-native-dotenvCreate .env file
Store credentials securely
# .env
DE_WORKSPACE_ID=your-workspace-id
DE_ACCESS_TOKEN=your-access-token
DE_ENVIRONMENT=prodConfigure babel.config.js
Enable env variables
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
['module:react-native-dotenv', {
moduleName: '@env',
path: '.env',
}]
]
}TypeScript Configuration
Add type definitions
Create types/env.d.ts:
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
Render MSI Component
App.tsx
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
Add Route
Use controls to create routes
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
Request Permissions
utils/permissions.ts
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
}Use Permissions
Request before using location
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
Stream-based Tracking
Real-time peer location updates
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
Order Operations
Using DClient.Order
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
// 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:
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:
npm start -- --reset-cacheBest 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

