Giphy SDK: Bridging Native SDKs in React Native

November 17, 2021 by Valentyn Pshoniuk

React Native is a popular framework for building native applications using React. This means that you can write declarative, component-based solutions that can run on many different platforms. To make it easier for React Native developers to implement GIPHY and let their users share GIFs, Clips, and more, we released the GIPHY SDK for React Native a few months ago.

The GIPHY RN SDK provides simple components and modules that can be used as complete solutions that entirely handle the GIPHY experience. React Native developers can also customize them and create more complex logic to meet their needs. Meanwhile, The SDK for React Native supports all GIPHY Content Types, including Clips.

Quick Start

The GIPHY RN SDK currently runs on Android and iOS platforms. You can find the requirements in our documentation.

Before working with GIPHY SDK, you need to get an SDK Key from the GIPHY Developer Portal. Please use a separate key for each platform (Android, iOS, Web) to which you add the GIPHY SDK.

To give it a try, you can play around with the GIPHY RN SDK example or integrate the GIPHY SDK into your playground/app with just a few lines of code:

import React from ‘react’
import { SafeAreaView, Button } from 'react-native'
import { GiphyDialog, GiphySDK } from '@giphy/react-native-sdk'

// Configure GIPHY SDK key
GiphySDK.configure({ apiKey: '*************' })

export default function App() {
  return (
    <SafeAreaView>
      <Button title="Show GIPHY Dialog" onPress={() => GiphyDialog.show()} />
    </SafeAreaView>
  )
}

Modules and components

The GIPHY RN SDK provides a set of components and modules that allow you to work with GIFs, Clips, Stickers, and more. Let’s take a brief look at them.

GiphySDK allows developers to configure SDK global parameters, such as SDK keys.

GiphyMediaView provides a painless way to display GIFs. For example, developers may be interested in showing selected GIFs in a message thread.

GiphyDialog allows users to select, search, or list trending GIFs, Clips, Stickers, Emojis. This is the best solution for developers who want to use a picker with all features out of the box. At the same time, GiphyDialog is highly customizable, allowing developers to adapt it to their needs. For example, you can show the confirmation screen, hide the suggestion bar, select a theme, specify a content rating for search results, and so on.

GiphyVideoView is a lightweight video player component with a simple API designed to work with clips, making clips integration a piece of cake.

GiphyVideoManager is helpful when you need to control the sound and playback of all clips simultaneously. For example, when users switch to another message thread in a chat app, it would be good to pause or mute all clips from the previous message thread.

GiphyGridView has many features with extensive customization options for rendering GIFs, Clips, Stickers in a grid. For example, you can control the number of columns in the grid, the distance between rendered GIFs, the direction of scrolling, and so forth. It is a building block for developers who want to create their own solutions with non-standard or complex logic, or highly customized UIs. So if you are one of them, this component will be a good starting point for you.

And last but not least, GiphyContent. This module contains methods for searching media by custom criteria. It will mainly interest developers who want to create their search components with a custom UI or logic.

Under the hood

Let’s take a  look at how the React Native library works behind the scenes.

Architecture

In React Native libraries, there are  two types of entities: modules and views. Modules can be used as regular JS objects or functions. Views are represented as React components.

At a low level, the GIPHY SDK for React Native wraps views and modules from the GIPHY native SDKs and exposes methods, properties, views, events, etc., to JS code using bridging files. This allows us to provide the same experience for all platforms, regardless of the technology used to create the app. 

On the JS side, we have two layers of abstraction. The first layer contains components that encapsulate the low-level logic that extracts modules and views from native code. The second layer contains platform-agnostic members that envelop the first layer elements and can add some functionality that doesn’t exist in native modules or some react-specific features. This approach makes it easier to support platform-dependent code and write generic solutions for platform-agnostic functionality. 

Project Structure

We used react-native-builder-bob to scaffold the project. This library provides an excellent starting place with everything you need to develop and release your library. The generated project structure is similar to all React Native libraries and looks like this:

The most interesting for us is the iOS, Android, and src folders. Let’s go deeper into the iOS part first.

This folder contains the Xcode project. Most of the files here are written in Swift. But React Native doesn’t support Swift directly. So we also have bridge files written in Objective C. These files simply open Swift interfaces for React Native.

This is what our Android folder looks like:

This folder contains the Gradle settings used to build the library and the source code, which, as you may have noticed, is mostly written in Kotlin.

iOS Implementation

Let’s take a look at how modules work on the iOS side. First, let’s just create a Swift class for the GIPHY SDK module with one method accessible for React. It must be inherited from NSObject so that it can be exposed to Obj-C:

import GiphyUISDK

@objc(RNGiphySDK)
class RNGiphySDK: NSObject {
  @objc
  public static func requiresMainQueueSetup() -> Bool {
    // this class must be initialized on the main thread
    return true
  }
  
  @objc(configure:)
  func configure(config: NSDictionary) -> Void {
	// implementation details
  }
}

Next, we have to expose RNGiphySDK class to React Native. To do this, we need to use some Obj-C Macros available in React Native.

#import <React/RCTBridgeModule.h>
#import "React/RCTEventEmitter.h"

@interface RCT_EXTERN_REMAP_MODULE(GiphyReactNativeSDK, RNGiphySDK, RCTEventEmitter)

RCT_EXTERN_METHOD(configure: (NSDictionary)config)

@end

In our case, we exposed the module under a different name. So we used the RCT_EXTERN_REMAP_MODULE macro. This is all you need to access the configure method of the RNGiphySDK class from JS. How it looks on the JS side, we’ll see later. 

To expose views from iOS to React Native, we need to do a bit more work compared to modules.  We don’t work with views directly. Instead, we use a ViewManager that returns an instance of our View. Let’s create the ViewManager for the GIPHY Media View.

@objc(RNGiphyMediaViewManager)
class RNGiphyMediaViewManager: RCTViewManager {
  override static func requiresMainQueueSetup() -> Bool {
    return true
  }
  
  override func view() -> UIView! {
    return RNGiphyMediaView()
  }
}

This manager simply inits and returns the GIPHY Media View. So let’s create the View itself with one property called media.

import UIKit
import GiphyUISDK

class RNGiphyMediaView: UIView {
  override init(frame: CGRect) {
    super.init(frame: frame)
  }

  required init?(coder aDecoder: NSCoder) {
    fatalError("init(coder:) has not been implemented")
  }
	
  // init the GPHMediaView from GIPHY iOS SDK
  lazy var mediaView: GPHMediaView = {
    return GPHMediaView()
  }()

  @objc func setMedia(_ rnValue: NSDictionary) -> Void {
	// this method will be called when React wants to update the media property of a component.
  }
 }

Next, similar to modules, we need to expose the GIPHY Media View in React Native using special macros.

#import "React/RCTViewManager.h"

@interface RCT_EXTERN_REMAP_MODULE(GiphyReactNativeMediaView, RNGiphyMediaViewManager, RCTViewManager)
  RCT_EXPORT_VIEW_PROPERTY(media, NSDictionary)

@end

Android Implementation

We need to do similar steps for Android. Let’s follow the same logic and integrate the Android modules and views into React Native. We will start with a Kotlin version of the GIPHY SDK module in addition to Swift.

package com.giphyreactnativesdk

import android.appcompat.app.AppCompatActivity
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.bridge.ReactContextBaseJavaModule
import com.facebook.react.bridge.ReadableMap

class GiphySDKModule(reactContext: ReactApplicationContext): ReactContextBaseJavaModule(reactContext) {
  override fun getName(): String {
    return "GiphyReactNativeSDK"
  }

  @ReactMethod
  fun configure(settings: ReadableMap) {
  	// implementation details
  }
 }

In this example, we exposed one method called configure. But to make it available from React Native, we also need to register this module in our package.

package com.giphyreactnativesdk

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


class GiphyReactNativeSdkPackage : ReactPackage {
  override fun createNativeModules(reactContext: ReactApplicationContext): List<NativeModule> {
    return listOf<NativeModule>(
      GiphySDKModule(reactContext),
    )
  }
 }

What about views? Just like with iOS, it’s more complex to implement the views than the modules. We also have a ViewManager that initiates and interacts with the view. First, let’s take a look at the view manager for the GIPHY Media View.

package com.giphyreactnativesdk

import com.facebook.react.bridge.ReadableMap
import com.facebook.react.uimanager.SimpleViewManager
import com.facebook.react.uimanager.ThemedReactContext
import com.facebook.react.uimanager.annotations.ReactProp


class GiphyMediaViewManager() : SimpleViewManager<GiphyRNMediaView>() {
  val REACT_CLASS = "GiphyReactNativeMediaView"

  companion object {
    val TAG = GiphyMediaViewManager::class.java.simpleName
  }

  @ReactProp(name = "media")
  fun setMedia(gifView: GiphyRNMediaView, rnMedia: ReadableMap?) {
    gifView.setMedia(rnMedia)
  }

  override fun getName(): String {
    return "GiphyReactNativeMediaView"
  }

  override fun createViewInstance(reactContext: ThemedReactContext): GiphyRNMediaView {
    return GiphyRNMediaView(reactContext)
  }
}

Unlike iOS view managers, the Android manager additionally registers component props with the ReactProp decorator and takes care of forwarding the props to the view. GIPHY Media View simply wraps the native view from the GIPHY Android SDK.

package com.giphyreactnativesdk

import android.content.Context
import android.util.AttributeSet
import com.facebook.react.bridge.ReadableMap
import com.giphy.sdk.core.GPHCore
import com.giphy.sdk.core.models.Media
import com.giphy.sdk.ui.views.GPHMediaView


class GiphyRNMediaView @JvmOverloads constructor(
  context: Context,
  attrs: AttributeSet? = null,
  defStyleAttr: Int = 0
) : GPHMediaView(context, attrs, defStyleAttr) {
    fun setMedia(rnMedia: ReadableMap?) {
	// implementation details
  }
}

And finally, the last step is to register our view in the package.

package com.giphyreactnativesdk

import android.view.View
import com.facebook.react.ReactPackage
import com.facebook.react.bridge.ReactApplicationContext
import com.facebook.react.uimanager.ReactShadowNode
import com.facebook.react.uimanager.ViewManager


class GiphyReactNativeSdkPackage : ReactPackage {
  override fun createViewManagers(reactContext: ReactApplicationContext):
    List<ViewManager<out View, out ReactShadowNode<*>>> {
    return listOf(
      GiphyMediaViewManager())
  }
}

JS Implementation

After all these steps, we can use our modules and views from JS. Thanks to React Native and our work with native code, interacting with them from javascript (or typescript) will be very easy.

import { NativeModules } from 'react-native'


// Configure GIPHY SDK key
export type NativeGiphySDKConfig = {
  apiKey: string
}

export interface INativeGiphySDK {
  configure(options: NativeGiphySDKConfig): void
}

export const NativeGiphySDK: INativeGiphySDK = NativeModules.GiphyReactNativeSDK

// Configure GIPHY SDK key
// NativeGiphySDK.configure({ apiKey: '*****' })

NativeGiphySDK contains all the methods from the GiphySDK native implementations. React Native takes care of what we need to use for iOS and Android. So next, we can use our module as a regular JS object and not think about low-level stuff. The same applies to components.

One last thing. We use TypeScript and provide high-level abstractions and types for all modules and views. This makes it easier for new developers to use our SDK and avoids misusing the API. 

Conclusions

The GIPHY RN SDK project has been an exciting experience for the GIPHY team with exciting results. Over the past couple of months, we have had thousands of installations of the GIPHY RN SDK via npm. We have even received the contributions to our project. It’s impressive to see how quickly the React Native community tries, integrates, and gives feedback for new libraries, and we’re excited to be able to offer the project to the community.

Useful links: