--- title: "React Native Accounts SDK" slug: "react-native-accounts-sdk" updated: 2024-10-21T09:48:54Z published: 2024-10-21T09:48:54Z canonical: "ignite.ticketmaster.com/react-native-accounts-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 Accounts SDK ## Accounts SDK #### Accounts SDK React Native (iOS) Create a new **.swift** file, the name in this guide is **"AccountsSDK"** but call it whatever you like 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 ``` In your **AccountsSDK.swift** create a class and extend **NSObject** like below (you can remove the auto-generated Foundation import): ```swift @objc(AccountsSDK) class AccountsSDK: NSObject { } ``` Make sure to include @objc(AccountsSDK) above the class to expose it to an **Objective-C** implementation file we will make later import **TicketmasterAuthentication** which should have been made available after adding the Ticketmaster Frameworks ```swift import TicketmasterAuthentication ``` Below are the Account SDK methods that React Native will call, you can add the desired methods to your **AccountsSDK** class and update them to your needs ```swift @objc(AccountsSDK) class AccountsSDK: NSObject { @objc public func configureAccountsSDK(_ resolve: @escaping (String) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { // build a combination of Settings and Branding let tmxServiceSettings = TMAuthentication.TMXSettings(apiKey: "[API_KEY]", region: .US) let branding = TMAuthentication.Branding(displayName: "My Team", backgroundColor: .red, theme: .light) let brandedServiceSettings = TMAuthentication.BrandedServiceSettings(tmxSettings: tmxServiceSettings, branding: branding) // configure TMAuthentication with Settings and Branding print("Accounts SDK Configuring...") TMAuthentication.shared.configure(brandedServiceSettings: brandedServiceSettings) { backendsConfigured in // your API key may contain configurations for multiple backend services // the details are not needed for most common use-cases print(" - Accounts SDK Configured: \(backendsConfigured.count)") resolve("Accounts SDK configuration successful") } failure: { error in // something went wrong, probably the wrong apiKey+region combination print(" - Accounts SDK Configuration Error: \(error.localizedDescription)") reject( "Accounts SDK Configuration Error:", error.localizedDescription, error as NSError) } } @objc public func login(_ resolve: @escaping ([String: Any]) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { TMAuthentication.shared.login { authToken in print("Login Completed") print(" - AuthToken: \(authToken.accessToken.prefix(20))...") let data = ["accessToken": authToken.accessToken] resolve(data) } aborted: { oldAuthToken, backend in let data = ["accessToken": ""] resolve(data) print("Login Aborted by User") } failure: { oldAuthToken, error, backend in print("Login Error: \(error.localizedDescription)") reject( "Accounts SDK Login Error", error.localizedDescription, error as NSError) } } @objc public func logout(_ resolve: @escaping (String) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { // logout of all accounts, not just the accounts in the current configuration TMAuthentication.shared.logoutAll {backends in resolve("Logout Successful") print("Logout Completed") print(" - Backends Count: \(backends?.count ?? 0)") } } @objc public func refreshToken(_ resolve: @escaping ([String: Any]) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { TMAuthentication.shared.validToken { authToken in print("Token Refreshed (if needed)") print(" - AuthToken: \(authToken.accessToken.prefix(20))...") let data = ["accessToken": authToken.accessToken] resolve(data) } aborted: { oldAuthToken, backend in print("Refresh Login Aborted by User") let data = ["accessToken": ""] resolve(data) } failure: { oldAuthToken, error, backend in print("Refresh Error: \(error.localizedDescription)") reject( "Accounts SDK Refresh Token Error", error.localizedDescription, error as NSError) } } @objc public func getMemberInfo(_ resolve: @escaping ([String: Any]) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { TMAuthentication.shared.memberInfo { memberInfo in print("MemberInfo Completed") print(" - UserID: \(memberInfo.localID ?? "")") print(" - Email: \(memberInfo.email ?? "")") print(memberInfo) let data = ["globalUserId": memberInfo.globalID, "memberId": memberInfo.localID, "hmacId": memberInfo.hmacID, "firstName": memberInfo.firstName, "lastName": memberInfo.lastName, "email": memberInfo.email, "phone": memberInfo.phone, "preferredLang": memberInfo.language] resolve(data as [String : Any]) } failure: { oldMemberInfo, error, backend in print("MemberInfo Error: \(error.localizedDescription)") reject( "Accounts SDK Member Info Error", error.localizedDescription, error as NSError) } } @objc public func getToken(_ resolve: @escaping ([String: Any]) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { TMAuthentication.shared.validToken(showLoginIfNeeded: false) { authToken in print("Token Retrieved") let data = ["accessToken": authToken.accessToken] resolve(data) } aborted: { oldAuthToken, backend in print("Token Retrieval Aborted ") let data = ["accessToken": ""] resolve(data) } failure: { oldAuthToken, error, backend in print("Token Retrieval Error: \(error.localizedDescription)") reject( "Accounts SDK Token Retrieval Error", error.localizedDescription, error as NSError) } } @objc public func isLoggedIn(_ resolve: @escaping ([String: Bool]) -> Void, reject: @escaping (_ code: String, _ message: String, _ error: NSError) -> Void) { TMAuthentication.shared.memberInfo { memberInfo in let hasToken = TMAuthentication.shared.hasToken() resolve(["result": hasToken]) } failure: { oldMemberInfo, error, backend in if(TMAuthentication.shared.hasToken()){ let hasToken = TMAuthentication.shared.hasToken() resolve(["result": hasToken]) } else { reject("Accounts SDK Is Logged In Error", error.localizedDescription, error as NSError) } } } } ``` Replace **[APIKEY]** in **configureAccountsSDK()** with your API key through your [developer account](https://developer-acct.ticketmaster.com/user/login?destination=user) The last step before you can call this method in React Native is to write an implementation file in **Objective-C** which will register these modules and methods to React Native Create a **.m** file called **AccountsSDK.m** (or whatever appropriate) and past in the below ```objectivec #import "React/RCTBridgeModule.h" @interface RCT_EXTERN_MODULE(AccountsSDK, NSObject) - (dispatch_queue_t)methodQueue { return dispatch_get_main_queue(); } + (BOOL)requiresMainQueueSetup { return true; } RCT_EXTERN_METHOD(configureAccountsSDK: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(login: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(logout: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(refreshToken: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(getMemberInfo: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(getToken: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) RCT_EXTERN_METHOD(isLoggedIn: (RCTPromiseResolveBlock)resolve reject:(RCTPromiseRejectBlock)reject) @end ``` **dispatch_get_main_queue** is a callback for secondary threads to inform the main thread that async operations are completed, it is used if your native module is rending any UI or to make sure your UI gets updated when you async actions are completed **requiresMainQueueSetup()** Ensure that your native module is run on the main thread which is relevant in case of UI interactions, if not provided this defaults to YES but a warning will come up in React Native as it will be default to NO in the future Finally, on the React Native side you can call the native methods in React Native using NativeModules ```typescript import { NativeModules } from 'react-native' ``` Here is examples of Accounts SDK methods being called in React Native: **Note:** the **N****ativeModules.AccountsSDK.configureAccountsSDK()** method needs to be called every time the app restarts and only needs to be called once each time the app restarts, it will probably work best in an **App.tsx** or earlier rendered file which is always rendered on app start up ```typescript import React, {useEffect} from 'react' import {View, NativeModules, Text, TouchableOpacity, StyleSheet} from 'react-native' type AccountsSDKError = { message: string code: string } const Home = () => { useEffect(() => { onConfigureAccountsSDK(); }, []) const onConfigureAccountsSDK = () => { NativeModules.AccountsSDK.configureAccountsSDK() .then((result: any) => { console.log(result) }) .catch((err: AccountsSDKError) => { console.log('Accounts SDK Configuration error:', err.message) }) } const onLogin = () => { NativeModules.AccountsSDK.login() .then((result: any) => { console.log('Accounts SDK Login access token:', result) }) .catch((err: AccountsSDKError) => { console.log('Accounts SDK Login error:', err.message) }) } const onLogout = () => { NativeModules.AccountsSDK.logout() .then((result: any) => { console.log(result) }) .catch(() => { console.log('Error on logout') }) } const onRefreshToken = () => { NativeModules.AccountsSDK.refreshToken() .then((result: any) => { console.log('Accounts SDK access token:', result) }) .catch((err: AccountsSDKError) => { console.log('Accounts SDK Refresh Token error:', err.message) }) } const getMemberInfo = () => { NativeModules.AccountsSDK.getMemberInfo() .then((result: any) => { console.log('Member Info:', result) }) .catch((err: AccountsSDKError) => { console.log('Member Info error:', err.message) }) } // Additional const getToken = async () => { try { // iOS getToken has the exact same Native logic as refreshToken, but will not display the login UI if a user is not logged in const result = await NativeModules.AccountsSDK.getToken(); console.log('Accounts SDK Login access token:', result); } catch (err) { console.log('Get Token error:', (err as Error).message); } } const isLoggedIn = async () => { try { const result = await AccountsSDK.isLoggedIn(); console.log('Is logged in: ', result.result); } catch (e: any) { !(e as Error).message.includes('User not logged in') && console.log('IsLoggedIn error: ', (e as Error).message); } } return ( onLogin()} style={styles.button}> Login onLogout()} style={styles.button}> Logout onRefreshToken()} style={styles.button}> Refresh Token getMemberInfo()} style={styles.button}> Get Member Info getToken()} style={styles.button}> Get Token isLoggedIn()} style={styles.button}> isLoggedIn ) } const styles = StyleSheet.create({ button: { backgroundColor: '#07a', width: 170, height: 30, borderRadius: 4, alignItems: 'center', margin: 4, justifyContent: 'center', }, }) export default Home ``` ### 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/docs/analytics](https://ignite.ticketmaster.com/docs/analytics) #### Accounts SDK React Native (Android) Create a native module in Android called **AccountsSDKModule.kt** with the following content: ```groovy class AccountsSDKModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { object IgniteSDKSingleton { private var authenticationSDK: TMAuthentication? = null fun getAuthenticationSDK(): TMAuthentication? { return authenticationSDK } fun setAuthenticationSDK(authSDK: TMAuthentication) { authenticationSDK = authSDK } } private val CODE = 1 override fun getName() = "AccountsSDK" private var mResultCallback: Callback? = null private val mActivityEventListener: ActivityEventListener = object : BaseActivityEventListener() { override fun onActivityResult( activity: Activity?, requestCode: Int, resultCode: Int, data: Intent? ) { if (mResultCallback != null) { mResultCallback!!.invoke(resultCode) mResultCallback = null } if (resultCode == Activity.RESULT_CANCELED) { } if (resultCode == Activity.RESULT_OK) { } } } init { reactContext.addActivityEventListener(mActivityEventListener) } @ReactMethod fun login(resultCallback: Callback) { IgniteSDKSingleton.getAuthenticationSDK()?.let { authentication -> runBlocking() { mResultCallback = resultCallback val currentFragmentActivity = currentActivity as FragmentActivity val intent = authentication.getLoginIntent(currentFragmentActivity) currentActivity?.startActivityForResult(intent, CODE) } } } @ReactMethod fun isLoggedIn(promise: Promise) = IgniteSDKSingleton.getAuthenticationSDK()?.let { authentication -> runBlocking { try { val response = withContext(context = Dispatchers.IO) { AuthSource.values().forEach { if (authentication.getToken(it)?.isNotBlank() == true) { return@withContext true } } return@withContext false } promise.resolve(response) } catch (e: Exception) { promise.reject("Accounts SDK isLoggedIn Error: ", e) } } } ?: false @ReactMethod fun configureAccountsSDK(promise: Promise) { runBlocking(Dispatchers.Main) { try { val currentFragmentActivity = currentActivity as FragmentActivity val authentication = TMAuthentication.Builder() .apiKey("") .clientName("My Team") .colors(createTMAuthenticationColors(android.graphics.Color.parseColor("#026cdf"))) .environment(TMXDeploymentEnvironment.Production) .region(TMXDeploymentRegion.US) .forceNewSession(true) .build(currentFragmentActivity) IgniteSDKSingleton.setAuthenticationSDK(authentication) TicketsSDKClient .Builder() .authenticationSDKClient(authentication) .colors(createTicketsColors(android.graphics.Color.parseColor("#026cdf"))) .build(currentFragmentActivity) .apply { TicketsSDKSingleton.setTicketsSdkClient(this) promise.resolve("Accounts SDK configuration successful") } } catch (e: Exception) { promise.reject("Accounts SDK Configuration Error: ", e) } } } @ReactMethod fun logout(promise: Promise) { runBlocking { withContext(context = Dispatchers.IO) { try { val currentFragmentActivity = currentActivity as FragmentActivity val authentication = TMAuthentication.Builder() .apiKey("") .clientName("My Team") .colors(TMAuthentication.ColorTheme()) .environment(TMXDeploymentEnvironment.Production) .region(TMXDeploymentRegion.US) .build(currentFragmentActivity) authentication.logout(currentFragmentActivity) promise.resolve(true) } catch (e: Exception) { promise.reject("Accounts SDK Logout Error: ", e) } } } } @ReactMethod fun getMemberInfo(promise: Promise) = IgniteSDKSingleton.getAuthenticationSDK()?.let { runBlocking { try { val archticsAccessToken = async { it.getToken(AuthSource.ARCHTICS) } val hostAccessToken = async { it.getToken(AuthSource.HOST) } val mfxAccessToken = async { it.getToken(AuthSource.MFX) } val sportXRAccessToken = async { it.getToken(AuthSource.SPORTXR) } val (resArchticsAccessToken, resHostAccessToken, resMfxAccessToken, resSportXRAccessToken) = awaitAll( archticsAccessToken, hostAccessToken, mfxAccessToken, sportXRAccessToken ) if (resArchticsAccessToken.isNullOrEmpty() && resHostAccessToken.isNullOrEmpty() && resMfxAccessToken.isNullOrEmpty() && resSportXRAccessToken.isNullOrEmpty()) { promise.resolve(null) } else { val memberInfoJson = Gson().toJson(it.fetchUserDetails().getOrNull()) promise.resolve(memberInfoJson) } } catch (e: Exception) { promise.reject("Accounts SDK getMemberInfo Error: ", e) } } } @ReactMethod fun refreshToken(promise: Promise) = IgniteSDKSingleton.getAuthenticationSDK()?.let { runBlocking { try { val hostAccessToken = async { it.getToken(AuthSource.HOST) } val archticsAccessToken = async { it.getToken(AuthSource.ARCHTICS) } val mfxAccessToken = async { it.getToken(AuthSource.MFX) } val sportXRAccessToken = async { it.getToken(AuthSource.SPORTXR) } val (resArchticsAccessToken, resHostAccessToken, resMfxAccessToken, resSportXRAccessToken) = awaitAll( archticsAccessToken, hostAccessToken, mfxAccessToken, sportXRAccessToken ) val tokenRefreshedParams: WritableMap = Arguments.createMap().apply { putString("accountsSdkTokenRefreshed", "accountsSdkTokenRefreshed") } val combinedTokens: WritableMap = Arguments.createMap() if (!resHostAccessToken.isNullOrEmpty()) { combinedTokens.putString("hostAccessToken", resHostAccessToken) } if (!resArchticsAccessToken.isNullOrEmpty()) { combinedTokens.putString("archticsAccessToken", resArchticsAccessToken) } if (!resMfxAccessToken.isNullOrEmpty()) { combinedTokens.putString("mfxAccessToken", resMfxAccessToken) } if (!resSportXRAccessToken.isNullOrEmpty()) { combinedTokens.putString("sportXRAccessToken", resSportXRAccessToken) } GlobalEventEmitter.sendEvent("igniteAnalytics", tokenRefreshedParams) promise.resolve(combinedTokens) } catch (e: Exception) { promise.reject("Accounts SDK refreshToken Error: ", e) } } } 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) ) ) @SuppressLint("ConflictingOnColor") private fun createTMAuthenticationColors(color: Int): TMAuthentication.ColorTheme = TMAuthentication.ColorTheme( // The Color class is part of the Compose library lightColors( primary = Color(color), primaryVariant = Color(color), secondary = Color(color), onPrimary = Color.White // Color used for text and icons displayed on top of the primary color. ), darkColors( primary = Color(color), primaryVariant = Color(color), secondary = Color(color), onPrimary = Color.White // Color used for text and icons displayed on top of the primary color. ) ) } ``` On the React Native side you can call the native methods in React Native using NativeModules ```typescript import { NativeModules } from 'react-native' ``` Most native methods returns a Promise use callbacks or async/await to get a return value. ```typescript