--- title: "Custom Modules" slug: "modules-overview-1" updated: 2026-04-13T21:10:08Z published: 2026-04-13T21:10:08Z canonical: "ignite.ticketmaster.com/modules-overview-1" --- > ## 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. # Custom Modules Custom modules let you embed branded content — headers, interactive buttons, and maps — directly in the ticket detail view. You can use them to surface parking information, venue maps, exclusive offers, or any other experience relevant to an event. Each module consists of: - A unique identifier string - An optional header view - Zero to three action buttons The SDK renders your modules alongside the prebuilt modules in the ticket detail view. --- ## Module delegate setup Register your module delegate before presenting the SDK. The delegate method is called once per event, giving you the event data you need to build context-aware modules. **iOS** Assign your delegate to `TMTickets.shared.moduleDelegate`, then implement `TMTicketsModuleDelegate`: ```swift extension MyClass: TMTicketsModuleDelegate {    func addCustomModules(event: TMPurchasedEvent, completion: @escaping ([TMTicketsModule]?) -> Void) {        let view = UIView()        view.backgroundColor = .red        let header = TMTicketsModule.HeaderDisplay(view: view)        let button1 = TMTicketsModule.ActionButton(title: "One")        let button2 = TMTicketsModule.ActionButton(title: "Two")        let button3 = TMTicketsModule.ActionButton(title: "Three")        let module = TMTicketsModule(            identifier: "com.myDemoApp.demoModule",            headerDisplay: header,            actionButtons: [button1, button2, button3])        completion([module])    } } ``` Call `completion` with your array of modules, or `completion(nil)` to show no custom modules for that event. **Android** Assign a `TicketsModuleDelegate` to `TicketsSDKSingleton.moduleDelegate`: ```kotlin TicketsSDKSingleton.moduleDelegate = object : TicketsModuleDelegate {    override fun getCustomModulesLiveData(order: Order): LiveData> {        val modules: ArrayList = ArrayList()        modules.add(getCustomModule(activity))        return MutableLiveData(modules)    }    override fun userDidPressActionButton(        buttonTitle: String?,        callbackValue: String?,        eventOrders: EventOrders?    ) {        // Handle button taps here    } } ``` The delegate returns `LiveData`, so you can post module updates asynchronously — for example, after fetching personalized data from your backend. --- ## Module identifier Every module requires a unique reverse-URL identifier. The SDK uses this string to distinguish your modules and route button callbacks. Use your app's bundle ID or a domain you control as the prefix: - `com.ticketmaster.seatUpgrades` - `com.nfl.broncosSeating` - `com.myDemoApp.parkingModule` --- ## Header views The header appears at the top of the module. It can show a solid color, a static image, or a map. **iOS** Pass a `UIView` to `TMTicketsModule.HeaderDisplay`. The SDK sizes the view using the aspect ratio you specify: | Aspect ratio | Description | | --- | --- | | `.automatic` | Derives ratio from the view's intrinsic content size | | `.square` | Forces a 1:1 ratio | | `.custom(ratio)` | Uses the ratio you provide | For an image header, pass a `UIImage` directly: ```swift let image = UIImage(named: "venueMap")! let header = TMTicketsModule.HeaderDisplay(image: image) ``` Use `TMTicketsModuleHeaderView` when you need text overlays on images or maps. Call `TMTicketsModuleHeaderView.build()` to get an instance, configure it, then wrap it in `TMTicketsModule.HeaderDisplay(view:)` **Android** Build your header using `ModuleBase` from the `TicketsSDKModule` helper class. Pass any `View` to `moduleBase.setHeader()`: ```kotlin fun getCustomModule(context: Context): ModuleBase {    val moduleBase = ModuleBase(context)    val view = View(context)    view.setBackgroundColor(ContextCompat.getColor(context, R.color.tickets_tm_brand_blue))    (view.layoutParams as ConstraintLayout.LayoutParams).apply {        height = context.dpToPx(200f).roundToInt()    }    moduleBase.setHeader(view)    moduleBase.setLeftButtonText("One")    moduleBase.setMiddleButtonText("Two")    moduleBase.setRightButtonText("Three")    return moduleBase } ``` You can also inflate a custom XML layout and pass it as the header: ```kotlin val headerView = LayoutInflater.from(context).inflate(R.layout.custom_module, null) moduleBase.setHeader(headerView) // Access child views via moduleBase.findViewById(R.id.myLabel) ``` ![Example custom module with colored header and three buttons](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/custom_module_example_1.jpg) --- ## Action buttons Each module supports up to three buttons. Tapping a button either triggers a callback to your app or opens a webpage inside the SDK. **iOS** Create an `ActionButton` with a title and an optional `callbackValue`. The SDK passes the callback value back to `handleModuleActionButton` so you can distinguish buttons without parsing button titles: ```swift func handleModuleActionButton(    event: TMPurchasedEvent,    module: TMTicketsModule,    button: TMTicketsModule.ActionButton,    completion: @escaping (TMTicketsModule.WebpageSettings?) -> Void ) {    guard module.identifier == "com.myDemoApp.demoModule" else { return }    switch button.callbackValue {    case "MyButton1":        completion(nil) // Dismiss the Tickets SDK    case "MyButton2":        let urlRequest = URLRequest(url: URL(string: "https://www.example.com")!)        let settings = TMTicketsModule.WebpageSettings(            pageTitle: "My Webpage",            urlRequest: urlRequest)        completion(settings) // Open a webpage inside the SDK    default:        break    } } ``` To open a webpage directly from the button definition instead of the callback, attach `WebpageSettings` at button creation time: ```swift let urlRequest = URLRequest(url: URL(string: "https://www.example.com")!) let settings = TMTicketsModule.WebpageSettings(pageTitle: "Details", urlRequest: urlRequest) let button = TMTicketsModule.ActionButton(title: "Learn More", webpageSettings: settings) ``` **Android** Set per-button click listeners on `ModuleBase`, or handle all taps centrally in `userDidPressActionButton`: ```kotlin // Custom listener on a specific button moduleBase.setLeftClickListener {    val url = "https://www.example.com"    context.startActivity(TmxWebUriHelper.openWebUriWebView(context, url)) } // Or handle via the delegate callback override fun userDidPressActionButton(    buttonTitle: String?,    callbackValue: String?,    eventOrders: EventOrders? ) {    when (callbackValue) {        "LeftClick" -> { /* handle left button */ }        "MiddleButton" -> { /* handle middle button */ }        "RightClick" -> { /* handle right button */ }    } } ``` ![Example module output showing three-button layout](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/Module_DefaultAspect.jpg) --- ## Map modules You can surface venue and parking maps as module headers. The SDK provides two approaches: a prebuilt venue directions module and fully custom map modules you build yourself. ### Prebuilt venue directions module The SDK includes an optional prebuilt Venue Directions module. On iOS it renders an Apple Maps thumbnail; on Android it shows a "Get Directions" button (thumbnail rendering is omitted on Android because Google charges per map tile request). Tapping either opens the native maps app with the venue pre-populated. Enable the Venue Directions module through your module delegate if you want the default behavior without any additional setup. ![Prebuilt venue directions module](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/DefaultMapModule.jpg) ![Apple Maps thumbnail on iOS](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/AppleMaps.jpg) ### Dynamic map modules Use a dynamic map when you want to show a specific location — for example, a parking lot or a stadium entrance — with a custom annotation. **iOS** `TMTicketsModuleHeaderView` renders a live `MKMapView` with an optional annotation. Configure the map region and annotation, then pass the view as the module header: ```swift func buildParkingModule(event: TMPurchasedEvent) -> TMTicketsModule? {    let mapRegion = MKCoordinateRegion(        center: CLLocationCoordinate2D(latitude: 34.0734, longitude: -118.2402),        span: MKCoordinateSpan(latitudeDelta: 0.02, longitudeDelta: 0.02))    let mapAnnotation = TMTicketsModuleHeaderView.MapAnnotation(        coordinate: CLLocationCoordinate2D(latitude: 34.0735, longitude: -118.2456),        title: "Lot 1")    let headerView = TMTicketsModuleHeaderView.build()    headerView.configure(        topLabelText: "Parking: Lot 1",        mapCoordinateRegion: mapRegion,        mapAnnotation: mapAnnotation)    let header = TMTicketsModule.HeaderDisplay(view: headerView as UIView)    let button = TMTicketsModule.ActionButton(title: "Directions to Parking")    return TMTicketsModule(        identifier: "com.mymlbappname.parkingModule",        headerDisplay: header,        actionButtons: [button]) } ``` **Android** Use a `MapView` from the Maps SDK for Android inside a custom layout. Set the map camera position and add a marker, then pass the inflated view to `moduleBase.setHeader()`: ```kotlin fun buildParkingModule(context: Context, activity: AppCompatActivity): ModuleBase {    val moduleBase = ModuleBase(context)    val mapView = MapView(context)    mapView.layoutParams = ConstraintLayout.LayoutParams(        ConstraintLayout.LayoutParams.MATCH_PARENT,        context.dpToPx(200f).roundToInt())    mapView.onCreate(null)    mapView.getMapAsync { googleMap ->        val lotLocation = LatLng(34.0735, -118.2456)        googleMap.addMarker(MarkerOptions().position(lotLocation).title("Lot 1"))        googleMap.moveCamera(CameraUpdateFactory.newLatLngZoom(lotLocation, 15f))        googleMap.uiSettings.isScrollGesturesEnabled = false    }    moduleBase.setHeader(mapView)    moduleBase.setLeftButtonText("Directions to Parking")    moduleBase.setLeftClickListener {        val uri = Uri.parse("geo:34.0735,-118.2456?q=Dodger+Stadium+Lot+1")        context.startActivity(Intent(Intent.ACTION_VIEW, uri))    }    return moduleBase } ``` > [!WARNING] > **Note:** Call `mapView.onResume()` and the corresponding lifecycle methods from your Fragment or Activity to ensure the map renders correctly. ![Dynamic map module showing parking lot annotation](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/DodgerStadiumParking.jpg) ### Static image map modules When you have a pre-rendered venue map image, use it as the module header and link to a full PDF or interactive map through a button. **iOS** ```swift func buildVenueMapModule(event: TMPurchasedEvent) -> TMTicketsModule? {    let mapImage = UIImage(named: "venueMap")!    let headerView = TMTicketsModuleHeaderView.build()    headerView.configure(backgroundImage: mapImage)    let header = TMTicketsModule.HeaderDisplay(view: headerView as UIView)    let urlRequest = URLRequest(url: URL(string: "https://example.com/venue-map.pdf")!)    let webpageSettings = TMTicketsModule.WebpageSettings(        pageTitle: "Venue Map",        urlRequest: urlRequest)    let button = TMTicketsModule.ActionButton(        title: "View Full Map",        webpageSettings: webpageSettings)    return TMTicketsModule(        identifier: "com.myappname.venueMap",        headerDisplay: header,        actionButtons: [button]) } ``` **Android** ```kotlin fun buildVenueMapModule(context: Context): ModuleBase {    val moduleBase = ModuleBase(context)    val imageView = ImageView(context)    imageView.layoutParams = ConstraintLayout.LayoutParams(        ConstraintLayout.LayoutParams.MATCH_PARENT,        context.dpToPx(200f).roundToInt())    imageView.scaleType = ImageView.ScaleType.CENTER_CROP    imageView.setImageResource(R.drawable.venue_map)    moduleBase.setHeader(imageView)    moduleBase.setLeftButtonText("View Full Map")    moduleBase.setLeftClickListener {        val url = "https://example.com/venue-map.pdf"        context.startActivity(TmxWebUriHelper.openWebUriWebView(context, url))    }    return moduleBase } ``` ![Static image map module with a](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/DisneylandModule.jpg) ![Webpage opened from map module button](https://cdn.document360.io/995f0bfa-740b-4247-b341-fde1e5326a96/Images/Documentation/DisneylandWebMap.jpg)