---
title: "React Native Tickets SDK"
slug: "react-native-tickets-sdk"
updated: 2024-11-14T16:17:26Z
published: 2024-11-14T16:17:26Z
canonical: "ignite.ticketmaster.com/react-native-tickets-sdk"
---

> ## Documentation Index
> Fetch the complete documentation index at: https://ignite.ticketmaster.com/llms.txt
> Use this file to discover all available pages before exploring further.

# React Native Tickets SDK

## Tickets SDK

#### Tickets SDK React Native (iOS)

Create a new **.swift** file, the name in this guide is **"****TicketsSdkViewController****"** but call it whatever you like

In React Native iOS there are two ways to present the Tickets SDK which are Embedded (inside a React Native View) or a Modal above the current screen, discussed later on. If it is the first time making a **.swift** file, Xcode will prompt you to make a **Bridging-Header.h** file, press "**Create Bridging Header**"

A **Bridging-Header.h** will expose Objective-C public headers to Swift In **[PROJECT-NAME]-Bridging-Header.h** file add

```objectivec
#import <React/RCTBridgeModule.h>
```

In your **TicketsViewController.swift** create a class and extend **UIViewController** like below (you can remove the auto-generated Foundation import):

```swift
class TicketsViewController: UIViewController {

}
```

import **TicketmasterAuthentication** and **TicketmasterTickets** which should have been made available after adding the Ticketmaster Frameworks

```swift
import TicketmasterAuthentication
import TicketmasterTickets
```

Override a **viewDidLoad** function

```swift
override func viewDidLoad() {
      super.viewDidLoad() 
    }
```

#### **Presenting the TicketsSDK**

In React Native iOS there are two ways to present the Tickets SDK which are **Embedded** (inside a React Native View) or a **Modal** above the current screen

#### **Embedded View Approach**

Declare TMTicketsView at the top of the class

```swift
class TicketsSdkViewController: UIViewController {
 var ticketsView: TMTicketsView!
```

In **viewDidLoad()** under super.viewDidLoad(), present the Embedded Tickets SDK View Controller by pasting in the following code

```swift
        TMTickets.shared.configure {
            print(" - Tickets SDK Configured")
            self.ticketsView = TMTicketsView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: self.view.frame.height))
            self.view.addSubview(self.ticketsView)
            TMTickets.shared.start(ticketsView: self.ticketsView)
        } failure: { error in
            print(" - Tickets SDK Configuration Error: \(error.localizedDescription)")
        }
```

#### **Modal View Approach**

In **viewDidLoad()** under super.viewDidLoad(), present the Embedded Tickets SDK View Controller by pasting in the following code

```swift
        TMTickets.shared.configure {
            print(" - Tickets SDK Configured")
            let ticketsVC = TMTicketsViewController()
            ticketsVC.modalPresentationStyle = .fullScreen
            self.present(ticketsVC, animated: false, completion: nil)
        } failure: { error in
            print(" - Tickets SDK Configuration Error: \(error.localizedDescription)")
        }
```

You can remove the line which sets the modal to **fullscreen** if you want a modal in which user can swipe down to dismiss

#### **Exposing Native View to React Native**

Create a new **.m** file, the name in this guide is **"****RNTTicketsSdkViewManager****"** you can change the name but note prefixing with "RNT" and appending "Manager" are common conventions and you could run into issues if you don't follow them. Paste in the below inside **RNTTicketsSdkViewManager.m**:

```objectivec
#import <React/RCTViewManager.h>
// Below import is eeded to use Swift files in Objective-C files i.e. TicketsSdkViewController
#import “[PROJECT-NAME]-Swift.h"

@interface RNTTicketsSdkViewManager : RCTViewManager
@end

@implementation RNTTicketsSdkViewManager

- (dispatch_queue_t)methodQueue
{
  return dispatch_get_main_queue();
}

+ (BOOL)requiresMainQueueSetup {
  return true;
}

RCT_EXPORT_MODULE(RNTTicketsSdkView)

- (UIView *)view
{
  TicketsSdkViewController *vc = [[TicketsSdkViewController alloc] init];
  return vc.view;
}

@end
```

#### **Show the View in React Native**

In your React Native project, create a file called **TicketsSdk.tsx** and paste in the below:

```typescript
import {requireNativeComponent} from 'react-native'

module.exports = requireNativeComponent('RNTTicketsSdkView')
```

#### **Embedded View approach**

Create a file called **MyEvents.tsx** and paste in the below:

```typescript
import React, {useCallback, useState} from 'react'
import {useFocusEffect} from '@react-navigation/native'
import TicketsSdk from '../components/TicketsSdk'

const MyTickets = () => {
return <TicketsSdk style={{flex: 1}} />
}

export default MyTickets
```

#### **Modal View Approach**

```typescript
import React, {useCallback, useState} from 'react'
import {View, Text, TouchableOpacity} from 'react-native'
import {useFocusEffect} from '@react-navigation/native'
import TicketsSdk from '../components/TicketsSdk'

const MyTickets = () => {
  const [initialFocus, setInitialFocus] = useState(false)
  const [showTicketsSdk, setShowTicketsSdk] = useState(false)

  useFocusEffect(
    useCallback(() => {
      // Show SDK's when screen focuses
      setInitialFocus(true)
      // unmount the RN components after the Native SDK's are launched,
      // so that the SDK can be reinitialized via RN's button onPress after the SDK's Native close button is pressed
      setTimeout(() => {
        setInitialFocus(false)
      }, 500)
      console.log('useFocusEffect mount called')
      return () => {
        setInitialFocus(false)
        console.log('useFocusEffect unmount called')
      }
    }, []),
  )

  const onShowTicketsSdk = () => {
    setShowTicketsSdk(true)
    setTimeout(() => {
      setShowTicketsSdk(false)
    }, 500)
  }

  return (
    <View style={{flex: 1}}>
      <View style={{margin: 4, width: 170, alignItems: 'center'}}>
        <Text>Tickets SDK</Text>
      </View>
      <TouchableOpacity
        onPress={() => onShowTicketsSdk()}
        style={{
          backgroundColor: '#07a',
          width: 170,
          height: 30,
          borderRadius: 4,
          alignItems: 'center',
          margin: 4,
          justifyContent: 'center',
        }}>
        <Text>Show Tickets SDK:</Text>
      </TouchableOpacity>
      {(initialFocus || showTicketsSdk) && (
        // Wrapper the SDK modal view in an additional view prevents the main view from being unresponsive after the SDK is presented
        <View>
          <TicketsSdk />
        </View>
      )}
    </View>
  )
}

export default MyTickets
```

### React Navigation (Embedded View Approach)

If you are using **React Navigation** you will have varying Header and Bottom Tab heights. in this case you can use **height** instead of **flex** and do the below:

```typescript
const [initialFocus, setInitialFocus] = useState(true);

 useEffect(() => {
    // Initially, the altered Bottom Tabs View frame height is not available for Native code on iOS, this becomes available after a rerender.
    // If needed an additional delay can be used before setting to false
    setInitialFocus(false);
  }, []);

  return <>{!initialFocus && <TicketsSdkEmbedded style={{height: '90%'}} />}</>;
```

You can then set a **height** on your bottom tabs so that height of the embedded view takes into account the height of your bottom tabs:

```typescript
const BottomTab = createBottomTabNavigator()

...

 <BottomTab.Navigator
  screenOptions={{
        tabBarStyle: {
...
        height: '10%',
...
        },
      }}>
```

Another approach would be to set the height in the **.swift** file instead of React Native. In this case, keep the changes to BottomTab.Navigator but instead of the solution involving initial focus include an extra line in **TicketsSdkViewController.swift**:

```swift
self.ticketsView = TMTicketsView.init(frame: CGRect(x: 0, y: 0, width: UIScreen.main.bounds.width, height: self.view.frame.height))
// extra line below
self.ticketsView.frame.size.height *= 0.9
// extra line above
self.view.addSubview(self.ticketsView)
```

### Analytics

To setup delegates in React Native iOS see: [https://ignite.ticketmaster.com/docs/react-native-accounts-information](https://ignite.ticketmaster.com/docs/react-native-accounts-information)

After setting up Delegates in your Accounts SDK **NSObject**/**RCTEventEmitter** class you can follow: [https://ignite.ticketmaster.com/v1/docs/analytics-ios-1](https://ignite.ticketmaster.com/v1/docs/analytics-ios-1)

### **React Native Tickets Page (Android)**

Create a custom view that extends FrameLayout. content of this view can be anything you want to render

```java
class CustomView(context: Context) : FrameLayout(context) {
  init {
    // set padding and background color
    setPadding(16,16,16,16)
    setBackgroundColor(Color.parseColor("#5FD3F3"))

    // add default text view
    addView(TextView(context).apply {
      text = "Welcome to Retail SDK with React Native."
    })
  }
}
```

Create a fragment with **FrameLayout**

```java
class TicketsFragment : Fragment() {

    private val mProgressDialog: AlertDialog by lazy {
        AlertDialog
            .Builder(this@MyFragment.requireActivity())
            .setView(LayoutInflater.from(this@MyFragment.requireActivity()).inflate(R.layout.layout_loading_view, null, false))
            .setCancelable(false)
            .create()
            .apply {
                setCanceledOnTouchOutside(false)
            }
    }

    private lateinit var customView: CustomView
    private fun createTicketsColors(color: Int): TicketsColors =
        TicketsColors(
            lightColors(primary = Color(color), primaryVariant = Color(color), secondary = Color(color)),
            darkColors(primary = Color(color), primaryVariant = Color(color), secondary = Color(color))
        )

    private suspend fun validateAuthToken(authentication: TMAuthentication): Map<AuthSource, String> {
        val tokenMap = mutableMapOf<AuthSource, String>()
        AuthSource.values().forEach {
            //Validate if there is an active token for the AuthSource, if not it returns null.
            authentication.getToken(it)?.let { token ->
                tokenMap[it] = token
            }
        }
        return tokenMap
    }

    private fun launchTicketsView() {
        mProgressDialog.dismiss()
        //Retrieve an EventFragment
        TicketsSDKSingleton.getEventsFragment(requireContext())?.let {
           childFragmentManager.beginTransaction().add(R.id.container, it).commit()
        }
    }

    private val resultLauncher = registerForActivityResult(
        ActivityResultContracts.StartActivityForResult()
    ) { result ->
        when (result.resultCode) {
            AppCompatActivity.RESULT_OK -> launchTicketsView()
            AppCompatActivity.RESULT_CANCELED -> {
            }
        }
    }

    override fun onAttach(context: Context) {
        super.onAttach(context)
        mProgressDialog.show()
        val coroutineScope = CoroutineScope(Dispatchers.IO)
        coroutineScope.launch {
            val authentication = TMAuthentication.Builder()
                .apiKey("[API_KEY]")
                .clientName("[TEAM_NAME]") // Team name to be displayed
                .colors(TMAuthentication.ColorTheme())
                .environment(TMXDeploymentEnvironment.Production) // Environment that the SDK will use. Default is Production
                .region(TMXDeploymentRegion.US) // Region that the SDK will use. Default is US
                .build(this@MyFragment.requireActivity())
            val tokenMap = validateAuthToken(authentication)

            TicketsSDKClient
                .Builder()
                .authenticationSDKClient(authentication) //Authentication object
                //Optional value to define the colors for the Tickets page
                .colors(createTicketsColors(android.graphics.Color.parseColor("#231F20")))
                //Function that generates a TicketsSDKClient object
                .build(this@MyFragment.requireActivity())
                .apply {
                    //After creating the TicketsSDKClient object, add it into the TicketsSDKSingleton
                    TicketsSDKSingleton.setTicketsSdkClient(this)

                    //Validate if there is an active token.
                    if (tokenMap.isNotEmpty()) {
                        //If there is an active token, it launches the event fragment
                        launchTicketsView()
                    } else {
                        //If there is no active token, it launches a login intent. Launch an ActivityForResult, if result
                        //is RESULT_OK, there is an active token to be retrieved.

                        val activity = this@MyFragment.activity // Use activity property instead of requireActivity()
                        //It is important to null check activity because 
                        //The method getLoginIntent(context: FragmentActivity) requires a non-null FragmentActivity as its parameter   
                        if (activity != null) {
                           resultLauncher.launch(TicketsSDKSingleton.getLoginIntent(activity))
                        } else {
                           // Handle the case where the activity is null
                           Log.e("MyFragment", "Activity is null, unable to launch login intent.")
                        }                  
                    }
                }
        }
    }

    override fun onCreateView(
        inflater: LayoutInflater, container: ViewGroup?,
        savedInstanceState: Bundle?
    ): View {
        customView = CustomView(requireNotNull(context))
        return customView // this CustomView could be any view that you want to render

    }
}
```

Create a ViewManager, this is used to bring the fragment to react native side.

```kotlin
class TicketsViewManager(
    private val reactContext: ReactApplicationContext
) : ViewGroupManager<FrameLayout>() {
  private var propWidth: Int? = null
  private var propHeight: Int? = null

  override fun getName() = REACT_CLASS

  /**
   * Return a FrameLayout which will later hold the Fragment
   */
  override fun createViewInstance(reactContext: ThemedReactContext) =
      FrameLayout(reactContext)

  /**
   * Map the "create" command to an integer
   */
  override fun getCommandsMap() = mapOf("create" to COMMAND_CREATE)

  /**
   * Handle "create" command (called from JS) and call createFragment method
   */
  override fun receiveCommand(
      root: FrameLayout,
      commandId: String,
      args: ReadableArray?
  ) {
    super.receiveCommand(root, commandId, args)
    val reactNativeViewId = requireNotNull(args).getInt(0)

    when (commandId.toInt()) {
      COMMAND_CREATE -> createFragment(root, reactNativeViewId)
    }
  }

  @ReactPropGroup(names = ["width", "height"], customType = "Style")
  fun setStyle(view: FrameLayout, index: Int, value: Int) {
    if (index == 0) propWidth = value
    if (index == 1) propHeight = value
  }

  /**
   * Replace your React Native view with a custom fragment
   */
  fun createFragment(root: FrameLayout, reactNativeViewId: Int) {
    val parentView = root.findViewById<ViewGroup>(reactNativeViewId)
    setupLayout(parentView)

    val myFragment = TicketsFragment()
    val activity = reactContext.currentActivity as FragmentActivity
    activity.supportFragmentManager
        .beginTransaction()
        .replace(reactNativeViewId, myFragment, reactNativeViewId.toString())
        .commit()
  }

  fun setupLayout(view: View) {
    Choreographer.getInstance().postFrameCallback(object: Choreographer.FrameCallback {
      override fun doFrame(frameTimeNanos: Long) {
        manuallyLayoutChildren(view)
        view.viewTreeObserver.dispatchOnGlobalLayout()
        Choreographer.getInstance().postFrameCallback(this)
      }
    })
  }

  /**
   * Layout all children properly
   */
  private fun manuallyLayoutChildren(view: View) {
    // propWidth and propHeight coming from react-native props
    val width = requireNotNull(propWidth)
    val height = requireNotNull(propHeight)

    view.measure(
        View.MeasureSpec.makeMeasureSpec(width, View.MeasureSpec.EXACTLY),
        View.MeasureSpec.makeMeasureSpec(height, View.MeasureSpec.EXACTLY))

    view.layout(0, 0, width, height)
  }

  companion object {
    private const val REACT_CLASS = "TicketsViewManager"
    private const val COMMAND_CREATE = 1
  }
}
```

Create a React Package and add your view manager

```java
class MyPackage : ReactPackage {

    override fun createViewManagers(
        reactContext: ReactApplicationContext
    ) = listOf(TicketsViewManager(reactContext))
}
```

Register the Package in your **MainApplication.kt**

```java
    override fun getPackages() = PackageList(this).packages.apply {
      add(MyPackage())
    }
```

Setup Javascript module

```javascript
import {requireNativeComponent} from 'react-native'

export const TicketsViewManager = requireNativeComponent('TicketsViewManager')
```

Create a custom View

```javascript
import React, {useEffect, useRef} from 'react';
import {
  PixelRatio,
  UIManager,
  findNodeHandle,
} from 'react-native';

import {TicketsViewManager} from './tickets-view-manager';

const createFragment = viewId =>
  UIManager.dispatchViewManagerCommand(
    viewId,
    // we are calling the 'create' command
    UIManager.TicketsViewManager.Commands.create.toString(),
    [viewId],
  );

export const TicketsView = () => {
  const ref = useRef(null);

  useEffect(() => {
    const viewId = findNodeHandle(ref.current);
    createFragment(viewId);
  }, []);

  return (
    <TicketsViewManager
      style={{
        // converts dpi to px, provide desired height
        height: PixelRatio.getPixelSizeForLayoutSize(200),
        // converts dpi to px, provide desired width
        width: PixelRatio.getPixelSizeForLayoutSize(200),
      }}
      ref={ref}
    />
  );
};
```
