Skip to main content

React Native Implementation Guide

This comprehensive guide covers everything you need to integrate social.plus Video SDK into your React Native application, including live streaming, video playback, and real-time notifications.

Prerequisites

  • React Native 0.64 or higher
  • Node.js 14 or higher
  • iOS 12+ (for iOS apps)
  • Android API level 21+ (for Android apps)
  • Valid social.plus API credentials

Installation & Setup

Package Installation

Install the required peer dependencies:
npm install \
  @amityco/ts-sdk-react-native \
  @amityco/video-broadcaster-react-native \
  @amityco/video-player-react-native \
  @api.video/react-native-livestream \
  react-native-video \
  react-native-vlc-media-player \
  react-native-get-random-values \
  react-native-rsa-native

Platform Configuration

1. Install iOS Dependencies

cd ios && pod install

2. Configure Permissions

Add the following permissions to your Info.plist file:
<key>NSCameraUsageDescription</key>
<string>This app needs camera access to broadcast live streams</string>

<key>NSMicrophoneUsageDescription</key>
<string>This app needs microphone access for live streaming audio</string>

<key>NSPhotoLibraryUsageDescription</key>
<string>This app needs photo library access to share images and videos</string>

3. Configure Build Settings

In your ios/Podfile, ensure minimum iOS version:
platform :ios, '12.0'

SDK Initialization

Basic Setup

import { Client } from '@amityco/ts-sdk-react-native';
import { setupAmityVideoPlayer } from '@amityco/video-player-react-native';

const initializeSocialPlus = async () => {
  try {
    // Initialize the SDK
    const loginParams = {
      userId: 'your-user-id',
      displayName: 'User Display Name',
      // Add other required parameters
    };

    const sessionHandler = {
      sessionWillRenewAccessToken: (renewal) => {
        // Handle session renewal
        renewal.renew();
      },
    };

    // Login to social.plus
    const response = await Client.login(loginParams, sessionHandler);
    
    // Setup video player after successful login
    setupAmityVideoPlayer();
    
    console.log('social.plus SDK initialized successfully');
  } catch (error) {
    console.error('Failed to initialize social.plus SDK:', error);
  }
};

Live Streaming Implementation

Broadcasting Live Streams

import React, { useRef, useState } from 'react';
import { View, Button, Alert } from 'react-native';
import { AmityVideoBroadcaster } from '@amityco/video-broadcaster-react-native';
import { StreamRepository } from '@amityco/ts-sdk-react-native';

const LiveBroadcastScreen = () => {
  const broadcasterRef = useRef(null);
  const [isStreaming, setIsStreaming] = useState(false);
  const [streamId, setStreamId] = useState(null);

  const startBroadcast = async () => {
    try {
      // Create a new stream
      const streamParams = {
        title: 'My Live Stream',
        description: 'Broadcasting live from React Native',
        thumbnailFileId: 'optional-thumbnail-file-id',
        isSecure: false,
      };

      const { data: newStream } = await StreamRepository.createStream(streamParams);
      setStreamId(newStream.streamId);

      // Start broadcasting
      broadcasterRef.current?.startPublish(newStream.streamId);
      setIsStreaming(true);
    } catch (error) {
      Alert.alert('Error', 'Failed to start broadcast');
      console.error('Broadcast error:', error);
    }
  };

  const stopBroadcast = () => {
    broadcasterRef.current?.stopPublish();
    setIsStreaming(false);
    setStreamId(null);
  };

  const switchCamera = () => {
    broadcasterRef.current?.switchCamera();
  };

  const onBroadcastStateChange = (state) => {
    console.log('Broadcast state changed:', state);
    // Handle state changes (connecting, connected, disconnected, etc.)
  };

  return (
    <View style={{ flex: 1 }}>
      <AmityVideoBroadcaster
        ref={broadcasterRef}
        style={{ flex: 1 }}
        onBroadcastStateChange={onBroadcastStateChange}
        // Optional: Configure video settings
        resolution={{
          width: 1280,
          height: 720,
        }}
        bitrate={2000000} // 2 Mbps
      />
      
      <View style={{ padding: 20 }}>
        <Button
          title={isStreaming ? "Stop Broadcast" : "Start Broadcast"}
          onPress={isStreaming ? stopBroadcast : startBroadcast}
        />
        
        {isStreaming && (
          <Button
            title="Switch Camera"
            onPress={switchCamera}
          />
        )}
      </View>
    </View>
  );
};

export default LiveBroadcastScreen;

Video Playback

import React, { useRef, useState, useEffect } from 'react';
import { View, Button, Text } from 'react-native';
import { AmityStreamPlayer } from '@amityco/video-player-react-native';
import { StreamRepository } from '@amityco/ts-sdk-react-native';

const VideoPlayerScreen = ({ streamId }) => {
  const playerRef = useRef(null);
  const [stream, setStream] = useState(null);
  const [isPlaying, setIsPlaying] = useState(false);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    if (streamId) {
      loadStream(streamId);
    }
  }, [streamId]);

  const loadStream = (id) => {
    const unsubscriber = StreamRepository.getStreamById(id, ({ data, loading, error }) => {
      setLoading(loading);
      
      if (data) {
        setStream(data);
        console.log('Stream loaded:', data.streamId, 'Status:', data.status);
      }
      
      if (error) {
        console.error('Error loading stream:', error);
      }
    });

    // Cleanup subscription
    return () => unsubscriber();
  };

  const playStream = () => {
    playerRef.current?.play();
    setIsPlaying(true);
  };

  const pauseStream = () => {
    playerRef.current?.pause();
    setIsPlaying(false);
  };

  const togglePlayback = () => {
    if (isPlaying) {
      pauseStream();
    } else {
      playStream();
    }
  };

  if (loading) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Loading stream...</Text>
      </View>
    );
  }

  if (!stream) {
    return (
      <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
        <Text>Stream not found</Text>
      </View>
    );
  }

  return (
    <View style={{ flex: 1 }}>
      <AmityStreamPlayer
        ref={playerRef}
        stream={stream}
        status={stream.status}
        style={{ flex: 1 }}
        onPlaybackStatusUpdate={(status) => {
          console.log('Playback status:', status);
        }}
      />
      
      <View style={{ padding: 20 }}>
        <Text style={{ marginBottom: 10 }}>
          {stream.title} - Status: {stream.status}
        </Text>
        
        <Button
          title={isPlaying ? "Pause" : "Play"}
          onPress={togglePlayback}
        />
      </View>
    </View>
  );
};

export default VideoPlayerScreen;

Push Notifications

For comprehensive push notification setup and handling, refer to our Push Notifications Guide.

Basic Notification Setup

import { StreamRepository } from '@amityco/ts-sdk-react-native';
import PushNotification from 'react-native-push-notification';

// Configure push notifications
const configurePushNotifications = () => {
  PushNotification.configure({
    onNotification: function(notification) {
      console.log('Notification received:', notification);
      
      // Handle stream-related notifications
      if (notification.data?.streamId) {
        // Navigate to stream or handle accordingly
        handleStreamNotification(notification.data);
      }
    },
    requestPermissions: Platform.OS === 'ios',
  });
};

const handleStreamNotification = (data) => {
  const { streamId, type } = data;
  
  switch (type) {
    case 'stream_started':
      // Handle stream started notification
      break;
    case 'stream_ended':
      // Handle stream ended notification
      break;
    default:
      console.log('Unknown notification type:', type);
  }
};

Error Handling & Troubleshooting

Common Issues and Solutions

// Common installation troubleshooting

// 1. Metro bundler issues with native dependencies
// Add to metro.config.js:
module.exports = {
  resolver: {
    assetExts: ['bin', 'txt', 'jpg', 'png', 'json', 'mp4', 'mov'],
  },
};

// 2. Android build issues
// Ensure proper Kotlin version in android/build.gradle:
buildscript {
  ext {
    kotlinVersion = "1.9.22" // Use this exact version
  }
}

// 3. iOS linking issues
// Run these commands:
// cd ios && pod deintegrate && pod install

// 4. Clear React Native cache
// npx react-native start --reset-cache

Performance Optimization

Best Practices

// Performance optimization tips

// 1. Optimize video quality based on device capabilities
const getOptimalVideoConfig = () => {
  const { width, height } = Dimensions.get('window');
  const isHighEnd = DeviceInfo.getTotalMemory() > 4000000000; // 4GB+ RAM
  
  if (isHighEnd && width > 1080) {
    return {
      resolution: { width: 1920, height: 1080 },
      bitrate: 4000000,
      frameRate: 30
    };
  } else if (width > 720) {
    return {
      resolution: { width: 1280, height: 720 },
      bitrate: 2000000,
      frameRate: 30
    };
  } else {
    return {
      resolution: { width: 854, height: 480 },
      bitrate: 1000000,
      frameRate: 24
    };
  }
};

// 2. Memory management for video components
const VideoComponent = React.memo(({ streamId }) => {
  useEffect(() => {
    return () => {
      // Cleanup video resources when component unmounts
      if (playerRef.current) {
        playerRef.current.cleanup?.();
      }
    };
  }, []);

  // Component implementation...
});

// 3. Optimize stream loading
const useStreamLoader = (streamId) => {
  const [stream, setStream] = useState(null);
  const [loading, setLoading] = useState(false);
  const cache = useRef(new Map());

  const loadStream = useCallback(async (id) => {
    // Check cache first
    if (cache.current.has(id)) {
      setStream(cache.current.get(id));
      return;
    }

    setLoading(true);
    try {
      const streamData = await StreamRepository.getStreamById(id);
      cache.current.set(id, streamData);
      setStream(streamData);
    } catch (error) {
      console.error('Failed to load stream:', error);
    } finally {
      setLoading(false);
    }
  }, []);

  return { stream, loading, loadStream };
};

Testing

Unit Testing Example

// __tests__/VideoSDK.test.ts
import { StreamRepository } from '@amityco/ts-sdk-react-native';
import { VideoSDKErrorHandler } from '../src/utils/VideoSDKErrorHandler';

// Mock the SDK
jest.mock('@amityco/ts-sdk-react-native');

describe('Video SDK Integration', () => {
  beforeEach(() => {
    jest.clearAllMocks();
  });

  test('should create stream successfully', async () => {
    const mockStream = {
      streamId: 'test-stream-id',
      title: 'Test Stream',
      status: 'idle'
    };

    StreamRepository.createStream.mockResolvedValue({ data: mockStream });

    const params = {
      title: 'Test Stream',
      description: 'Test Description'
    };

    const result = await StreamRepository.createStream(params);
    expect(result.data).toEqual(mockStream);
    expect(StreamRepository.createStream).toHaveBeenCalledWith(params);
  });

  test('should handle stream creation error', async () => {
    const mockError = new Error('Network error');
    StreamRepository.createStream.mockRejectedValue(mockError);

    const errorMessage = VideoSDKErrorHandler.handleStreamError(mockError);
    expect(errorMessage).toBeDefined();
  });
});

Integration Testing

// __tests__/VideoPlayerIntegration.test.tsx
import React from 'react';
import { render, fireEvent, waitFor } from '@testing-library/react-native';
import VideoPlayerScreen from '../src/screens/VideoPlayerScreen';

describe('Video Player Integration', () => {
  test('should load and play stream', async () => {
    const mockStreamId = 'test-stream-id';
    
    const { getByText, getByTestId } = render(
      <VideoPlayerScreen streamId={mockStreamId} />
    );

    // Wait for stream to load
    await waitFor(() => {
      expect(getByText('Test Stream')).toBeTruthy();
    });

    // Test play button
    const playButton = getByText('Play');
    fireEvent.press(playButton);

    await waitFor(() => {
      expect(getByText('Pause')).toBeTruthy();
    });
  });
});

Advanced Features

Custom UI Components

// Custom broadcaster component with advanced features
const CustomBroadcaster = ({ onStreamStart, onStreamEnd }) => {
  const [filters, setFilters] = useState([]);
  const [overlays, setOverlays] = useState([]);

  const addFilter = (filterType) => {
    setFilters(prev => [...prev, filterType]);
  };

  const addOverlay = (overlayData) => {
    setOverlays(prev => [...prev, overlayData]);
  };

  return (
    <View style={{ flex: 1 }}>
      <AmityVideoBroadcaster
        ref={broadcasterRef}
        style={{ flex: 1 }}
        filters={filters}
        overlays={overlays}
        onStreamStart={onStreamStart}
        onStreamEnd={onStreamEnd}
      />
      
      {/* Custom filter controls */}
      <FilterControls onFilterChange={addFilter} />
      
      {/* Custom overlay controls */}
      <OverlayControls onOverlayAdd={addOverlay} />
    </View>
  );
};

Troubleshooting

For detailed troubleshooting guides, see:

Common React Native Issues

  1. Metro bundler conflicts: Clear cache with npx react-native start --reset-cache
  2. iOS build failures: Run cd ios && pod deintegrate && pod install
  3. Android permission issues: Ensure all required permissions are declared in AndroidManifest.xml
  4. Video playback issues: Check device compatibility and network connectivity

Next Steps

Support

Need help? Contact our support team or visit our community forum for assistance with React Native implementation.