React Native

👍

Prerequisites

  1. React Native CLI
  2. Node.js (v18 or above)
  3. Android Studio and Xcode installed
  4. A working React Native project

Installing the SDK

Android Setup

Platform-specific Configuration

  • Create GipChatService.kt and GipChatModule.kt under app/src/main/java/com/<YourProjectName>
  • import io.gupshup.gipchat.GipChat inside MainApplication.kt and use it like add(GipChatModule())
package com.awesomeproject
import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.NativeModule
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager

class GipChatModule: ReactPackage {
    override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
        val modules = ArrayList<NativeModule>()
        modules.add(GipChatService(reactContext))
        return modules
    }

    override fun createViewManagers(reactContext: ReactApplicationContext): List<ViewManager<View, ReactShadowNode<*>>> {
        return emptyList()
    }
}
package com.awesomeproject
import com.facebook.react.bridge.Callback
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReactMethod
import io.gupshup.gipchat.GipChat
import io.gupshup.gipchat.listener.GipChatListener
import kotlinx.serialization.json.Json
import kotlinx.serialization.json.jsonObject

class GipChatService(private val reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) {
    private val gipChat=GipChat
    private val listener = Listener()
    init {
        gipChat.setListener(listener)
    }
    override fun getName(): String {
        return "GipChatModule"
    }

    @ReactMethod
    fun setAppId(appId: String) {
        gipChat.setAppId(appId)
    }

    @ReactMethod
    fun setUserName(userName: String) {
        gipChat.setUserName(userName)
    }

    @ReactMethod
    fun setUserId(userId: String) {
        gipChat.setUserId(userId)
    }

    @ReactMethod
    fun initialize(callback: Callback?) {
        gipChat.initialize(reactContext) { initialized ->
            callback?.invoke(initialized)
        }
    }

    @ReactMethod
    fun onError(callback: Callback?) {
        listener.message = {message ->
            callback?.invoke(message)
        }
    }

    @ReactMethod
    fun show() {
        gipChat.show()
    }

    @ReactMethod
    fun close() {
        gipChat.close()
    }

    @ReactMethod
    fun setListener(listener: GipChatListener) {
        gipChat.setListener(listener)
    }

    inner class Listener: GipChatListener {
        var message: (String) -> Unit = {}
        override fun onError(exception: Exception) {
            exception.message?.let {
                try {
                    val json = Json.parseToJsonElement(it).jsonObject
                    if (json["message"] != null) {
                        message(json["message"].toString().replace("\"", ""))
                        return
                    }
                } catch (e: Exception) {
                    message(it)
                    return
                }
            }
            message(exception.message ?: "Error occurred..")
        }
        override fun onInitialized() {
            //println("Initialized")
        }

    }
}
package com.awesomeproject
import android.app.Application
import com.facebook.react.PackageList
import com.facebook.react.ReactApplication
import com.facebook.react.ReactHost
import com.facebook.react.ReactNativeHost
import com.facebook.react.ReactPackage
import com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load
import com.facebook.react.defaults.DefaultReactHost.getDefaultReactHost
import com.facebook.react.defaults.DefaultReactNativeHost
import com.facebook.soloader.SoLoader
import io.gupshup.gipchat.GipChat

class MainApplication : Application(), ReactApplication {

  override val reactNativeHost: ReactNativeHost =
      object : DefaultReactNativeHost(this) {
        override fun getPackages(): List<ReactPackage> =
            PackageList(this).packages.apply {
              // Packages that cannot be autolinked yet can be added manually here, for example:
              // add(MyReactNativePackage())
                add(GipChatModule())
            }

        override fun getJSMainModuleName(): String = "index"

        override fun getUseDeveloperSupport(): Boolean = BuildConfig.DEBUG

        override val isNewArchEnabled: Boolean = BuildConfig.IS_NEW_ARCHITECTURE_ENABLED
        override val isHermesEnabled: Boolean = BuildConfig.IS_HERMES_ENABLED
      }

  override val reactHost: ReactHost
    get() = getDefaultReactHost(applicationContext, reactNativeHost)

  override fun onCreate() {
    super.onCreate()
    SoLoader.init(this, false)
    if (BuildConfig.IS_NEW_ARCHITECTURE_ENABLED) {
      // If you opted-in for the New Architecture, we load the native entry point for this app.
      load()
    }
  }
}

iOS Native Setup

  • Complete the steps required for the iOS Native Setup first before proceeding.
  • Create a swift file named "RNGipChat.swift" and paste the below code.
import Foundation
import React
import GipKit


@objc(RNGipChat)
class RNGipChat: NSObject {


   let gipChat = GipChat.shared
    let delegate = Delegate()
  override init() {
   gipChat.setDelegate(delegate)
 }
    @objc
   static func requiresMainQueueSetup() -> Bool {
       return true
   }


   @objc
   func setAppId(_ appId: String) {
     gipChat.setAppId(appId)
   }


   @objc
   func setUserName(_ userName: String) {
     gipChat.setUserName(userName)
   }


   @objc
   func setUserId(_ userId: String) {
     gipChat.setUserId(userId)
   }


   @objc
     func initialize(_ callback: @escaping RCTResponseSenderBlock) {
       gipChat.initialize { success in
             callback([NSNull(), success])
         }
     }
    @objc
   func onError(_ callback: @escaping RCTResponseSenderBlock) {
     delegate.errorMessage = { message in
       callback([NSNull(), message])
     }
   }


   @objc
   func show() {
     gipChat.show()
   }


   @objc
   func close() {
     gipChat.close()
   }
  class Delegate: GipChatDelegate {
     var errorMessage: (String) -> Void = { message in }
    
     func onError(message: String) {
         errorMessage(message)
     }
 }
}

Initializing the SDK

  • Create a GipChat.tsx service file add all the methods to expose to the App.tsx
import {NativeModules, Platform} from 'react-native';


const GipService = NativeModules.GipChatModule;
const GipIOSService = NativeModules.RNGipChat;


const setAppId = (appId: string) => {
 if (Platform.OS === 'android') {
   GipService.setAppId(appId);
 } else {
   GipIOSService.setAppId(appId);
 }
};

// Set User Name only if available

const setUserName = (userName: string) => {
 if (Platform.OS === 'android') {
   GipService.setUserName(userName);
 } else {
   GipIOSService.setUserName(userName);
 }
};

// Set User ID only if available

const setUserId = (userId: string) => {
 if (Platform.OS === 'android') {
   GipService.setUserId(userId);
 } else {
   GipIOSService.setUserId(userId);
 }
};


const initialize = (callback: (success: boolean) => void) => {
 if (Platform.OS === 'android') {
   GipService.initialize((success: boolean) => {
     callback(success);
   });
 } else {
   GipIOSService.initialize((error: any, success: boolean) => {
     if (error) {
       console.error('Initialization failed:', error);
       callback(false);
     } else {
       callback(success);
     }
   });
 }
};


const onError = (callback: (message: string) => void) => {
 if (Platform.OS === 'android') {
   GipService.onError((message: string) => {
     callback(message)
   });
 } else {
   GipIOSService.onError((error: any, message: string) => {
     callback(message)
   });
 }
}


const show = () => {
 if (Platform.OS === 'android') {
   GipService.show();
 } else {
   GipIOSService.show();
}
};
export default {setAppId, setUserName, setUserId, initialize, show, onError};

Using the SDK Features

  • After successful initialization, you can use the SDK's features in your React Native components(App.tsx).
import React, {useState} from 'react';
import {
 SafeAreaView,
 StyleSheet,
 Text,
 TouchableOpacity,
 View,
} from 'react-native';
import {ActivityIndicator, TextInput} from '@react-native-material/core';
import GipChat from './GipChat';


function App(): React.JSX.Element {
 const [isLoading, setIsLoading] = useState(false);
 const [appId, setAppId] = useState('c5453fbf-95e6-4330-9e14-28a85d3ea6a5');
 const [userName, setUserName] = useState('Gaurav');
 const [userId, setUserId] = useState('Test-User-Id-1234');
 const [initialized, setInitialized] = useState(false);


 const handleInitialize = () => {
   if (!appId && !userName && !userId) {
     console.log('Please fill the fields');
     return;
   }
   setIsLoading(true);
   GipChat.setAppId(appId);
   GipChat.setUserName(userName);
   GipChat.setUserId(userId);
   GipChat.initialize((success: boolean) => {
     if (success) {
       console.log('Initialization success :: ', success);
       setInitialized(true);
     } else {
     //  console.log('Something went wrong!!');
     }
     setIsLoading(false);
   });
   GipChat.onError((message: string) => {
     console.log(message);
   });
 };


 const handleStartChat = () => {
   setIsLoading(true);
   GipChat.show();
   setIsLoading(false);
 };


 return (
   <SafeAreaView style={styles.container}>
     <View style={{height: 150, alignItems: 'center', paddingTop: 50}}>
       <Text style={styles.title}>GipSDK Demo Test</Text>
     </View>


     <View style={styles.form}>
       <TextInput
         style={styles.input}
         label="App Id"
         value={appId}
         onChangeText={setAppId}
       />
       <TextInput
         style={styles.input}
         label="User Name"
         value={userName}
         onChangeText={setUserName}
         color="#904a3f"
       />
       <TextInput
         style={styles.input}
         label="User Id"
         value={userId}
         onChangeText={setUserId}
         color="#904a3f"
       />
       <View style={{alignItems: 'center', marginTop: 10}}>
         <TouchableOpacity
           style={styles.buttonContainer}
           onPress={handleInitialize}>
           <Text style={styles.text}>
             {initialized ? 'Re-Initialize' : 'Initialize'}
           </Text>
         </TouchableOpacity>
       </View>
     </View>


     <View style={{flex: 1, justifyContent: 'center'}}>
       <TouchableOpacity
         style={{...styles.buttonContainer, opacity: initialized ? 1 : 0.4}}
         onPress={handleStartChat}
         disabled={isLoading || !initialized}>
         {isLoading ? (
           <ActivityIndicator animating={isLoading} />
         ) : (
           <Text style={styles.text}>Start Chat</Text>
         )}
       </TouchableOpacity>
     </View>
   </SafeAreaView>
 );
}


const styles = StyleSheet.create({
 container: {
   flex: 1,
   alignItems: 'center',
   backgroundColor: '#fff',
 },
 form: {
   width: '80%',
 },
 label: {
   marginTop: 5,
   marginBottom: 2,
 },
 input: {
   borderBottomColor: '#999',
   marginBottom: 10,
 },
 buttonContainer: {
   height: 45,
   flexDirection: 'row',
   justifyContent: 'center',
   alignItems: 'center',
   marginBottom: 20,
   borderRadius: 30,
   backgroundColor: '#f8f2f4',
   width: 200,
   shadowColor: 'rgba(0,0,0, .99)', // IOS
   shadowOffset: {height: 1, width: 1}, // IOS
   shadowOpacity: 1, // IOS
   shadowRadius: 1, //IOS
   elevation: 1, // Android
 },
 title: {
   fontSize: 24,
   color: '#000',
   fontWeight: 'bold',
 },
 text: {
   color: '#8b4d43',
   fontSize: 16,
 },
});


export default App;

Testing and Debugging

Android Testing

Run the app on an Android emulator or device:

npx react-native run-android

iOS Testing

Run the app on an iOS emulator or device:

npx react-native run-ios

Debugging

  • Use console.log for debugging.
  • Use the React Native Debugger or Flipper for more advanced debugging features.