Third Party Integrations
Ticketmaster Integration
Rover includes support for integrating with the Ticketmaster Ignite SDK. This will allow your team to use Ticketmaster identity information in the Rover platform.
The integration is indirect; you integrate both the Ignite SDK and the Rover SDK into your app, and write a little bit of glue code.
SDK Authentication
To enable additional data access for personalization use cases, you will also need to enable SDK Authentication. This is necessary to securely verify the signed-in user. You can provide the ID token JWT issued by Ignite directly; see SDK Authentication with Ticketmaster for setup and token refresh on iOS and Android.
Ticketmaster ID Format
Ticketmaster IDs are often issued in a three-part, dot-separated form:
teamname.17804314.1852513
Rover expects only the middle segment (17804314 in the example above). Before submitting an ID to the Rover Ticketmaster module, always split it on . and take the middle piece. If the ID is not in the three-part form, pass it through unchanged.
The iOS and Android sections below each include a small helper function that performs this parsing; use it whenever you call setTicketmasterID (iOS) or setTicketmasterId (Android).
Installing the Module
iOS
First, be sure that the "RoverTicketmaster" package product module has been selected from the SwiftPM package (review the Install page for details). The RoverTicketmaster module has dependencies on the TicketmasterDiscoveryAPI and TicketmasterPurchase modules of the Ticketmaster Ignite SDK, which will be added by the SPM automatically.
Then you need to configure a TMAuthenticationDelegate in your view controller hosting the Ignite SDK (as described in the Ignite SDK documentation), and then from there you'll call methods on Rover's TicketmasterAuthorizer object to keep Rover up to date on Ticketmaster logins.
First, set the user's Ticketmaster credentials into Rover after a successful sign-in with the Ignite SDK. Implement the onStateChanged(backend:state:error:) method in the delegate, check for the .loginCompleted state and call setTicketmasterID method and passing in the user's TM ID from the TMAuthentication.shared.memberInfo.
As described in Ticketmaster ID Format, parse the ID before passing it to Rover, using a helper function like this:
func parseTicketmasterID(_ rawID: String) -> String {
let components = rawID.split(separator: ".", omittingEmptySubsequences: false)
guard components.count == 3, !components[1].isEmpty else {
return rawID
}
return String(components[1])
}
extension MyViewController: TMAuthenticationDelegate {
func onStateChanged(
backend: TMAuthentication.BackendService?,
state: TMAuthentication.ServiceState,
error: (Error)?) {
// ...
switch state {
// ...
case .loginCompleted:
TMAuthentication.shared.memberInfo { memberInfo in
guard let localID = memberInfo.localID else { return }
Rover.shared.ticketmasterAuthorizer.setTicketmasterID(
parseTicketmasterID(localID)
)
} failure: { oldMemberInfo, error, backend in
print("MemberInfo Error: \(error.localizedDescription)")
}
}
}
}
Then clear the user's Ticketmaster credentials after a successful sign-out with the Ignite SDK documentation. Implement the onStateChanged(backend:state:error:) method in the delegate, check for the .loggedOut state and call clearCredentials method.
extension MyViewController: TMAuthenticationDelegate {
//...
func onStateChanged(
backend: TMAuthentication.BackendService?,
state: TMAuthentication.ServiceState,
error: Error?
) {
// ...
switch state {
// ...
case .loggedOut:
Rover.shared.ticketmasterAuthorizer.clearCredentials()
}
}
}
In order to capture Ticket Selection and Purchase events, you need to setup a TMPurchaseUserAnalyticsDelegate in your view controller hosting the Ignite SDK (as described in the Ignite SDK documentation), and then from there you'll call methods on Rover's TicketmasterAnalytics object to keep Rover up to date on Ticketmaster events.
extension MyViewController: TMPurchaseUserAnalyticsDelegate {
//...
func userDidPerform(
action: TMTickets.Analytics.Action,
metadata: TMTickets.Analytics.MetadataType
) {
Rover.shared.ticketmasterAnalytics.postTicketmasterAction(
action: action,
metadata: metadata
)
}
func userDidView(
page: TMTickets.Analytics.Page,
metadata: TMTickets.Analytics.MetadataType
) {
Rover.shared.ticketmasterAnalytics.postTicketmasterEvent(
page: page,
metadata: metadata
)
}
func purchaseNavigationController(
_ purchaseNavigationController: TMPurchaseNavigationController,
didBeginCheckoutFor event: DiscoveryEvent
) {
Rover.shared.ticketmasterAnalytics.didBeginCheckout(for: event)
}
func purchaseNavigationController(
_ purchaseNavigationController: TMPurchaseNavigationController,
didEndCheckoutFor event: DiscoveryEvent,
because reason: TMEndCheckoutReason
) {
Rover.shared.ticketmasterAnalytics.didEndCheckout(for: event, because: reason)
}
func purchaseNavigationController(
_ purchaseNavigationController: TMPurchaseNavigationController,
didBeginTicketSelectionFor event: DiscoveryEvent
) {
Rover.shared.ticketmasterAnalytics.didBeginTicketSelection(for: event)
}
func purchaseNavigationController(
_ purchaseNavigationController: TMPurchaseNavigationController,
didEndTicketSelectionFor event: DiscoveryEvent,
because reason: TMEndTicketSelectionReason
) {
Rover.shared.ticketmasterAnalytics.didEndTicketSelection(for: event, because: reason)
}
}
Android
Add the Rover Ticketmaster module to your Gradle dependencies:
implementation "io.rover.sdk:ticketmaster:4.x.x"
Latest SDK Version
Use the same SDK version for all Rover modules and find available versions on the GitHub releases page.
Initialize the TicketmasterAssembler:
Rover.initialize(
...,
TicketmasterAssembler(),
...
)
Then you need to implement the PresenceLoginListener interface in an Activity, have the TmxLoginNotifier instance register the login listener and then from there use methods on Rover's TicketmasterAuthorizer object to keep Rover up to date on Ticketmaster logins.
First, set the user's Ticketmaster credentials into Rover after a successful sign-in with the Ignite SDK. Implement the onMemberUpdated(backendName: TMLoginApi.BackendName, member: UserInfoManager.MemberInfo?) method and call the setTicketmasterId method passing in the user's TM ID from the MemberInfo object.
As described in Ticketmaster ID Format, parse the ID before passing it to Rover, using a helper function like this:
fun parseTicketmasterId(rawId: String): String {
val components = rawId.split(".")
return if (components.size == 3 && components[1].isNotEmpty()) components[1] else rawId
}
fun setupAnalytics() {
TmxLoginNotifier.getInstance().registerLoginListener(this)
}
override fun onMemberUpdated(backendName: TMLoginApi.BackendName, member: UserInfoManager.MemberInfo?) {
member?.let {
Rover.shared.ticketmasterAuthorizer.setTicketmasterId(
parseTicketmasterId(member.getGlobalId())
)
}
}
Then clear the user's Ticketmaster credentials after a successful sign-out with the Ignite SDK. Implement the onLogoutSuccessful() method and call the clearCredentials method.
fun setupAnalytics() {
TmxLoginNotifier.getInstance().registerLoginListener(this)
}
override fun onLogoutSuccessful(backendName: TMLoginApi.BackendName) {
Rover.shared.ticketmasterAuthorizer.clearCredentials()
}
In order to capture Ticket Selection and Purchase events, you need to setup a TMPurchaseUserAnalyticsListener when setting up an TMPurchaseFragmentFactory, with the Ignite SDK (as described in the Ignite SDK documentation), and then from there you'll call methods on Rover's TicketmasterAnalytics object to keep Rover up to date on Ticketmaster events.
class MyActivity : AppCompatActivity(), TMPurchaseUserAnalyticsListener {
//...
override fun onTicketSelectionStarted(event: DiscoveryEvent) {
Rover.shared.ticketmasterAnalytics.onTicketSelectionStarted(event)
}
override fun onTicketSelectionFinished(event: DiscoveryEvent, reason: TMTicketSelectionEndReason) {
Rover.shared.ticketmasterAnalytics.onTicketSelectionFinished(event, reason)
}
override fun onCheckoutStarted(event: DiscoveryEvent) {
Rover.shared.ticketmasterAnalytics.onCheckoutStarted(event)
}
override fun onCheckoutFinished(event: DiscoveryEvent, reason: TMCheckoutEndReason) {
Rover.shared.ticketmasterAnalytics.onCheckoutFinished(event, reason)
}
}
Other Ticketmaster events can be captured by observing the UserAnalyticsDelegate LiveData and posting the event to the Rover SDK.
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
//...
UserAnalyticsDelegate.handler.getLiveData().observeForever(userAnalyticsObserver)
}
private val userAnalyticsObserver = Observer<UserAnalyticsDelegate.AnalyticsData?> {
it?.let {
//Post the event to the Rover SDK
Rover.ticketmasterAnalytics.postTicketmasterEvent(it.actionName, it.data)
}
}
SDK Authentication with Ticketmaster
Requires Rover SDK 4.10.0 or later.
You can use the ID token JWT issued by Ticketmaster Ignite to authenticate Rover SDK requests for personalized ticketing data. In your mobile app channel's SDK authentication settings in Rover, choose Ticketmaster. Rover uses Ticketmaster's public key, so you do not need to generate your own signing key or add a custom ticketmasterID claim to this token.
Your workspace must also have its Ticketmaster data integration configured. Rover verifies the Ticketmaster token and matches its email to a customer in that integration. Use an ID token issued by https://auth.ticketmaster.com; not every authentication backend or login flow supplies one.
Pass the complete idToken string unchanged. Do not pass accessToken, a refresh token, or a parsed Ticketmaster member ID. The parsing described in Ticketmaster ID Format applies only to the member ID sent to the Ticketmaster module, not to the JWT. Continue to synchronize that member ID and clear the module's credentials as described below; SDK Authentication is a separate credential.
Token lifecycle
After initializing Rover and configuring Ignite:
- Register Rover's refresh callback once with an object retained for the lifetime of your signed-in session.
- After successful sign-in, or after restoring an existing Ignite session, obtain its ID token and set it on Rover before loading personalized data. Rover does not invoke the refresh callback when no token has been set.
- When Rover requests a refresh, silently refresh Ignite's credentials and pass the resulting ID token back through Rover's setter. Rover requests this when the JWT expires within 60 seconds and can wait up to 10 seconds for the setter. This callback does not return a token.
- If your app refreshes Ignite independently, synchronize the resulting ID token with Rover too.
- Clear Rover's SDK Authentication token when the session ends. Prevent an outstanding refresh from setting it again after logout or an account change.
The examples force a refresh when starting a session so that a restored token is not already close to expiry. If you already have an ID token from a successful Ignite login callback, you can supply it to Rover directly instead.
Ignite's normal retrieval calls can return cached tokens. The refresh callback forces a refresh rather than simply returning that cache. The examples pass the returned ID token to Rover without decoding its JWT claims. Rover checks the stored token's expiry before subsequent requests, and the server verifies its signature and validity.
iOS SDK Authentication
The example uses TicketmasterAuthentication, RoverFoundation, and RoverData. Keep the bridge alive, pass the backend used for the user's sign-in, and call its methods on the main thread. The refresh callback uses fetchRule: .forceFetch and showLoginIfNeeded: false, so it does not present login UI while Rover is waiting.
import TicketmasterAuthentication
import RoverFoundation
import RoverData
final class TicketmasterRoverAuthentication {
private let backend: TMAuthentication.BackendService
private let onTokenReady: () -> Void
private let onTokenUnavailable: () -> Void
private var sessionActive = false
private var generation = 0
private var refreshing = false
init(
backend: TMAuthentication.BackendService,
onTokenReady: @escaping () -> Void = {},
onTokenUnavailable: @escaping () -> Void
) {
self.backend = backend
self.onTokenReady = onTokenReady
self.onTokenUnavailable = onTokenUnavailable
Rover.shared.registerTokenRefreshRequestCallback { [weak self] in
self?.synchronizeToken(forceRefresh: true)
}
}
// Call after successful sign-in or restoration of an Ignite session.
func sessionStarted() {
generation += 1
sessionActive = true
refreshing = false
Rover.shared.clearSDKAuthenticationIDToken()
synchronizeToken(forceRefresh: true)
}
// Also call after your app independently refreshes Ignite's credentials.
func synchronizeToken(forceRefresh: Bool = false) {
guard sessionActive, !refreshing else { return }
refreshing = true
let requestGeneration = generation
TMAuthentication.shared.validToken(
backend: backend,
showLoginIfNeeded: false,
fetchRule: forceRefresh ? .forceFetch : .fetchIfEmptyOrOld
) { [weak self] authToken in
guard let self, self.generation == requestGeneration else { return }
self.refreshing = false
guard let token = authToken.idToken else {
self.onTokenUnavailable()
return
}
Rover.shared.setSDKAuthenticationIDToken(token)
self.onTokenReady()
} aborted: { [weak self] _, _ in
self?.tokenUnavailable(for: requestGeneration)
} failure: { [weak self] _, _, _ in
self?.tokenUnavailable(for: requestGeneration)
}
}
// Call as soon as logout or an account change starts.
func sessionEnded() {
generation += 1
sessionActive = false
refreshing = false
Rover.shared.clearSDKAuthenticationIDToken()
}
private func tokenUnavailable(for requestGeneration: Int) {
guard generation == requestGeneration else { return }
refreshing = false
onTokenUnavailable()
}
}
Use the backend configured for the current account rather than relying on Ignite's automatic backend selection in a multi-backend app. You can also supply authToken.idToken directly to Rover.shared.setSDKAuthenticationIDToken(_:) from an existing Ignite login or refresh success callback, provided it belongs to the current signed-in session.
Android SDK Authentication
The forced-refresh overload below requires Ticketmaster Authentication 3.19.0 or later. getTMAuthToken(source) is available in 3.18.0, but that version does not expose the getToken(source, forceRefresh) overload used here. Use the same AuthSource that your app configured and authenticated, such as HOST, ARCHTICS, or SPORTXR.
Keep the bridge and its coroutine scope alive for the session, and call its methods on the main thread and use a main-thread coroutine scope. For example, an Activity that owns the signed-in session can pass its lifecycleScope. The callback launches a coroutine; it must not block Rover's main-thread callback while waiting for Ignite.
import com.ticketmaster.authenticationsdk.AuthSource
import com.ticketmaster.authenticationsdk.TMAuthentication
import io.rover.sdk.core.Rover
import io.rover.sdk.core.clearSdkAuthorizationIdToken
import io.rover.sdk.core.registerSdkAuthorizationIdTokenRefreshCallback
import io.rover.sdk.core.setSdkAuthorizationIdToken
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Job
import kotlinx.coroutines.launch
class TicketmasterRoverAuthentication(
private val authentication: TMAuthentication,
private val source: AuthSource,
private val scope: CoroutineScope,
private val onTokenReady: () -> Unit = {},
private val onTokenUnavailable: () -> Unit
) {
private var sessionActive = false
private var generation = 0
private var refreshJob: Job? = null
init {
Rover.shared.registerSdkAuthorizationIdTokenRefreshCallback {
synchronizeToken(forceRefresh = true)
}
}
// Call after successful sign-in or restoration of an Ignite session.
fun sessionStarted() {
sessionEnded()
sessionActive = true
synchronizeToken(forceRefresh = true)
}
// Also call after your app independently refreshes Ignite's credentials.
fun synchronizeToken(forceRefresh: Boolean = false) {
if (!sessionActive || refreshJob?.isActive == true) return
val requestGeneration = generation
refreshJob = scope.launch {
try {
if (forceRefresh &&
authentication.getToken(source, forceRefresh = true) == null
) {
if (generation == requestGeneration) onTokenUnavailable()
return@launch
}
// getToken above triggers refresh; its ACCESS token is not sent to Rover.
val idToken = authentication.getTMAuthToken(source)?.idToken
if (generation != requestGeneration) return@launch
if (idToken == null) {
onTokenUnavailable()
return@launch
}
Rover.shared.setSdkAuthorizationIdToken(idToken)
onTokenReady()
} catch (error: CancellationException) {
throw error
} catch (error: Exception) {
if (generation == requestGeneration) onTokenUnavailable()
}
}
}
// Call as soon as logout or an account change starts.
fun sessionEnded() {
generation += 1
sessionActive = false
refreshJob?.cancel()
refreshJob = null
Rover.shared.clearSdkAuthorizationIdToken()
}
}
getToken(source, forceRefresh = true) triggers Ignite's refresh flow and returns an access token. Retrieve the ID token separately with getTMAuthToken(source)?.idToken after that call completes. Never use the access token as a substitute when the ID token is missing.
Refresh failures and sign-out
onTokenReady signals that an ID token has been set on Rover; use it to enable personalized data loading or retry a pending operation.
onTokenUnavailable is an app-owned handler for a missing ID token or a failed silent refresh. Use it to offer retry or route the user to your normal sign-in flow. Do not automatically open sign-in UI from Rover's refresh callback, and do not submit oldAuthToken from Ignite's failure or aborted callbacks as a newly refreshed token.
If no token is supplied within 10 seconds, Rover continues the request without the SDK Authentication token. Treat that as an authentication failure for protected data; do not assume the request will succeed. Once sign-in or refresh succeeds, set the new token and retry the operation. When the session is confirmed to have ended, call sessionEnded() and clear the Ticketmaster module's credentials too.
The ID token is optional in Ignite, and compiling these APIs does not guarantee that a particular backend issues a token compatible with Rover. Confirm the issuer and ID-token availability for your client with Ticketmaster if the field is empty or authentication is rejected.
For more about the native APIs, see Ticketmaster's authentication examples and the Ignite SDK API documentation.