Skip to content

React Native SDK Examples

Real-world examples using de.sdk-rn based on actual implementation.

Example Apps

Complete examples showing:

  • Basic Map Integration - Rendering MSI component
  • Live Location Tracking - Using handles.myLocation()
  • Peer Tracking - Driver/agent location updates
  • Fleet Management - Multiple entity tracking
  • Navigation - Turn-by-turn routing
  • Order Management - Using DClient

Complete App Example

Full implementation from real codebase.

App Structure

typescript
// App.tsx
import { Stack } from 'expo-router'
import { View, StyleSheet, ActivityIndicator, TouchableOpacity, AppState } from 'react-native'
import React, { useRef, useEffect, useState } from 'react'
import { Auth, MSI, Utils, type MSIRef, type MSIInterface } from '@de./sdk-rn'

const credentials = {
  remoteOrigin: 'https://your-app.com',
  context: 'your-context-token',
  cid: 'your-connector-id',
  secret: 'your-connector-secret'
}

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

export default function Index() {
  const msiRef = useRef<MSIRef>(null)
  const [isLoading, setIsLoading] = useState(true)
  const [error, setError] = useState<string | null>(null)
  const [isReady, setIsReady] = useState(false)
  const hasInitializedRef = useRef(false)

  // Fetch token on mount
  useEffect(() => {
    async function initialize() {
      try {
        const token = await auth.getToken()
        if (!token) {
          setError('Failed to get access token')
          setIsLoading(false)
          return
        }
        setError(null)
      } catch (err) {
        setError(err instanceof Error ? err.message : 'Authentication failed')
      } finally {
        setIsLoading(false)
      }
    }

    initialize()

    return () => {
      console.log('[MSI] Component unmounting, stopping auto-refresh')
      auth.stopAutoRefresh()
    }
  }, [])

  // Handle MSI loaded
  const handleLoaded = async ({ handles, controls }: MSIInterface) => {
    if (hasInitializedRef.current) {
      console.log('[MSI] Already initialized, skipping')
      return
    }
  
    console.log('[MSI] Loaded - initializing')
    hasInitializedRef.current = true
    
    try {
      // Your app logic here
      await initializeMap({ handles, controls })
    } catch (error) {
      console.error('[MSI] Error:', error)
      setError(error instanceof Error ? error.message : 'Initialization failed')
    }
  }

  const handleRetry = () => {
    setError(null)
    hasInitializedRef.current = false
    setIsReady(false)
    msiRef.current?.retry()
  }

  if (isLoading) {
    return (
      <View style={styles.center}>
        <ActivityIndicator size="large" />
      </View>
    )
  }

  if (error) {
    return (
      <View style={styles.center}>
        <Text style={styles.error}>{error}</Text>
        <TouchableOpacity onPress={handleRetry}>
          <Text>Retry</Text>
        </TouchableOpacity>
      </View>
    )
  }

  return (
    <View style={styles.container}>
      <MSI
        ref={msiRef}
        env="prod"
        getAccessToken={() => auth.accessToken || ''}
        onReady={() => setIsReady(true)}
        onLoaded={handleLoaded}
        onError={(error) => setError(error.message)}
      />
    </View>
  )
}

const styles = StyleSheet.create({
  container: { flex: 1 },
  center: { flex: 1, justifyContent: 'center', alignItems: 'center' },
  error: { color: 'red', marginBottom: 16 }
})

Example 1: Basic Route Display

Create and display a route on the map.

typescript
async function initializeMap({ controls }: MSIInterface) {
  // Pin current location
  const currentLocation = await controls.pinCurrentLocation()
  console.log('Current location:', currentLocation)
  
  // Set route
  await controls.setRoute({
    routeId: 'delivery-1',
    origin: {
      coords: { lng: -0.1173, lat: 5.6413 },
      caption: { label: 'Pickup' }
    },
    destination: {
      coords: { lng: -0.1247, lat: 5.6209 },
      caption: { label: 'Delivery' }
    },
    options: {
      profile: 'driving-traffic',
      animation: 'dot:flow',
      pointless: true // Hide waypoint markers
    }
  })
  
  // Fit route in view
  await controls.fitRoutesBounds({ margin: 100 })
}

Example 2: Live Location Tracking

Track user's live location using handles.

typescript
function setupLiveLocation(handles: MSIInterface['handles']) {
  const stream = handles.myLocation('client')
  
  if (!stream) {
    console.error('Failed to create location stream')
    return
  }
  
  stream
    .on('data', (location) => {
      console.log('Live location:', location)
      // Send to backend
      sendLocationUpdate(location)
    })
    .onerror((error) => {
      console.error('Location error:', error)
    })
    .onclose(() => {
      console.log('Location stream closed')
    })
  
  // Return cleanup function
  return () => stream.close()
}

Example 3: Peer Location Tracking

Track driver/agent location with real-time updates.

typescript
function trackDriverLocation(
  handles: MSIInterface['handles'],
  initialPosition: { lng: number, lat: number, heading: number }
) {
  // Create peer stream
  const peerStream = handles.peerLocation(initialPosition, {
    label: 'Driver',
    duration: 5,
    unit: 'min'
  })
  
  if (!peerStream) return
  
  // Create live stream for updates
  const liveStream = new Utils.Stream()
  
  // Listen to WebSocket for driver updates
  const ws = new WebSocket('wss://api.dedot.io')
  
  ws.onmessage = (event) => {
    const data = JSON.parse(event.data)
    
    if (data.type === 'driver:location') {
      liveStream.sync({
        position: {
          lng: data.location.lng,
          lat: data.location.lat,
          heading: data.bearing
        },
        caption: {
          duration: data.eta,
          unit: 'min',
          label: 'Driver arriving'
        }
      })
    }
  }
  
  // Pipe updates to peer stream
  liveStream.pipe(peerStream)
  
  // Cleanup
  return () => {
    ws.close()
    liveStream.close()
  }
}

Example 4: Fleet Management

Track multiple vehicles using nearby handles.

typescript
function setupFleetTracking(
  handles: MSIInterface['handles'],
  vehicles: Array<{
    id: string
    location: { lng: number, lat: number, heading: number }
  }>
) {
  const entities = vehicles.map(v => ({
    id: v.id,
    status: 'ACTIVE' as const,
    grade: '1H' as const,
    type: 'car' as const,
    currentLocation: v.location
  }))
  
  const nearbyStream = handles.nearby(entities)
  
  if (!nearbyStream) return
  
  const liveStream = new Utils.Stream()
  
  // Setup live controls
  nearbyStream
    .live(async (controls) => {
      // Add new vehicle
      await controls.add({
        id: 'new-vehicle',
        status: 'ACTIVE',
        grade: '1H',
        type: 'car',
        currentLocation: {
          lng: -0.1801,
          lat: 5.6853,
          heading: 90
        }
      })
      
      // Move existing vehicle
      const moving = setInterval(async () => {
        await controls.move({
          id: vehicles[0].id,
          position: {
            lng: vehicles[0].location.lng += 0.0001,
            lat: vehicles[0].location.lat += 0.0001,
            heading: 45
          }
        })
      }, 1000)
      
      // Remove vehicle after 10 seconds
      setTimeout(async () => {
        await controls.remove('new-vehicle')
        clearInterval(moving)
      }, 10000)
    })
    .pipe(liveStream)
  
  // Cleanup
  return () => {
    nearbyStream.close()
    liveStream.close()
  }
}

Example 5: Turn-by-Turn Navigation

Implement navigation with real-time position updates.

typescript
async function startNavigation(
  handles: MSIInterface['handles'],
  journey: {
    routeId: string
    origin: { lng: number, lat: number }
    destination: { lng: number, lat: number }
    waypoints?: Array<{ lng: number, lat: number }>
  }
) {
  const navStream = await handles.navigation({
    routeId: journey.routeId,
    origin: {
      coords: journey.origin,
      caption: { label: 'Start' }
    },
    destination: {
      coords: journey.destination,
      caption: { label: 'End' }
    },
    waypoints: journey.waypoints?.map(w => ({
      coords: w,
      caption: { label: 'Stop' }
    })),
    options: {
      profile: 'driving-traffic',
      mode: 'navigation'
    }
  })
  
  if (!navStream) {
    throw new Error('Failed to create navigation stream')
  }
  
  const liveStream = new Utils.Stream()
  
  // Track user position with GPS
  const watchId = navigator.geolocation.watchPosition(
    (position) => {
      liveStream.sync({
        position: {
          lng: position.coords.longitude,
          lat: position.coords.latitude,
          heading: position.coords.heading || 0
        }
      })
    },
    (error) => console.error('GPS error:', error),
    {
      enableHighAccuracy: true,
      distanceFilter: 10,
      interval: 2000
    }
  )
  
  // Pipe position updates to navigation
  liveStream.pipe(navStream)
  
  // Listen to navigation events
  navStream
    .on('data', (data) => {
      console.log('Navigation update:', data)
    })
    .onerror((error) => {
      console.error('Navigation error:', error)
    })
  
  // Cleanup
  return () => {
    navigator.geolocation.clearWatch(watchId)
    liveStream.close()
  }
}

Example 6: Order Management

Create and manage orders using DClient.

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

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

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

async function createDeliveryOrder(
  pickup: { address: string, coords: { lng: number, lat: number } },
  delivery: { address: string, coords: { lng: number, lat: number } },
  customerId: string
) {
  try {
    // Generate intent token
    const intentToken = await orderClient.intent(customerId)
    
    // Add waypoints
    const waypoints = await orderClient.addWaypoint([
      {
        no: 1,
        type: 'pickup',
        description: pickup.address,
        coordinates: pickup.coords,
        contact: {
          type: 'business',
          reference: 'restaurant-1',
          phone: '+1234567890'
        }
      },
      {
        no: 2,
        type: 'dropoff',
        description: delivery.address,
        coordinates: delivery.coords,
        contact: {
          type: 'client',
          reference: customerId,
          phone: '+0987654321'
        }
      }
    ], intentToken)
    
    // Add package details
    await orderClient.addPackage({
      waypointNo: 1,
      careLevel: 2,
      category: 'food',
      weight: 2.5,
      note: 'Handle with care'
    }, intentToken)
    
    // Set service options
    await orderClient.setService({
      'fees.total.amount': 15.99,
      'fees.total.currency': 'USD',
      'payment.mode': 'card',
      'payment.paid': true,
      xpress: 'standard'
    }, intentToken)
    
    // Update stage
    await orderClient.updateStage('assigned', intentToken)
    
    return { intentToken, waypoints }
  } catch (error) {
    console.error('Order creation failed:', error)
    throw error
  }
}

Example 7: Search and Geocoding

Search for places and geocode addresses.

typescript
async function searchAndSelect(controls: MSIInterface['controls']) {
  // Search for place
  const suggestions = await controls.searchQuery(
    'Accra Mall',
    { lng: -0.1769, lat: 5.6037 } // Near this location
  )
  
  console.log('Suggestions:', suggestions)
  // [{ id, name, description, distance }, ...]
  
  // Select first result
  const selected = await controls.searchSelect(0)
  console.log('Selected place:', selected)
  // { name, location: { lng, lat }, address }
  
  // Resolve address to coordinates
  const coords = await controls.resolvePlace('Madina, Accra, Ghana')
  console.log('Coordinates:', coords)
  
  // Reverse geocode coordinates
  const place = await controls.resolveCoordinates('-0.176,5.683')
  console.log('Place:', place)
}

Example 8: Interactive Location Picker

Let user pick location by dragging.

typescript
async function pickLocation(
  controls: MSIInterface['controls'],
  handles: MSIInterface['handles']
) {
  // Enable drag-pick mode
  await controls.enableDragPickLocation({
    lng: -0.1769,
    lat: 5.6037
  })
  
  // Update content during drag
  await controls.setDragPickContent('duration', {
    time: 5,
    unit: 'min'
  })
  
  // Listen for user pick
  handles.onPickLocation((location) => {
    console.log('User picked:', location)
    // { point: { x, y }, coordinates: { lng, lat } }
    
    // Disable picker
    controls.disableDragPickLocation()
    
    // Use the picked location
    saveLocation(location.coordinates)
  })
}

Complete Delivery App

Full app combining all features.

typescript
function DeliveryApp() {
  const msiRef = useRef<MSIRef>(null)
  const [order, setOrder] = useState(null)
  
  const handleLoaded = async ({ controls, handles }: MSIInterface) => {
    // 1. Get current location
    const location = await controls.pinCurrentLocation()
    
    // 2. Create order
    const orderData = await createDeliveryOrder(
      { address: 'Restaurant', coords: { lng: -0.1173, lat: 5.6413 } },
      { address: 'Customer', coords: location! },
      'customer-123'
    )
    setOrder(orderData)
    
    // 3. Set route
    await controls.setRoute({
      routeId: orderData.intentToken,
      origin: { coords: { lng: -0.1173, lat: 5.6413 } },
      destination: { coords: location! },
      options: { profile: 'driving-traffic', animation: 'dot:flow' }
    })
    
    // 4. Track driver
    const cleanup = trackDriverLocation(handles, {
      lng: -0.1173,
      lat: 5.6413,
      heading: 0
    })
    
    // 5. Setup live location
    const cleanupLocation = setupLiveLocation(handles)
    
    return () => {
      cleanup?.()
      cleanupLocation?.()
    }
  }
  
  return (
    <MSI
      ref={msiRef}
      env="prod"
      getAccessToken={() => auth.accessToken || ''}
      onLoaded={handleLoaded}
    />
  )
}

Best Practices

Do

  • Use Auth with autorefresh enabled
  • Clean up streams when unmounting
  • Handle errors gracefully
  • Use refs for MSI component
  • Store credentials securely
  • Test on real devices

Avoid

  • Hardcoding credentials
  • Creating multiple MSI instances
  • Forgetting stream cleanup
  • Ignoring error callbacks
  • Testing only on simulators
  • Blocking the main thread

Next Steps