Skip to main content
Logo
Explore APIsContact Us
  • Home
  1. Resources
  2. Virtual Stadium
  3. Chat Component

Chat Component

Get started quickly with the Virtual Stadium Chat component by following this comprehensive integration guide. This guide covers the essential steps to integrate real-time messaging, bet sharing, flash bets, and AI-powered insights into your iOS application.

#Overview

To integrate the Chat component:

  1. Add the ChatView component to your SwiftUI view hierarchy
  2. Configure authentication with JWT tokens and channel information
  3. Implement optional features (bet sharing, flash bets, insights)
  4. Customize the appearance and behavior as needed
  5. Handle user interactions and callbacks

This guide will walk you through each step with code examples and best practices.


#Basic Integration

#SwiftUI

Add the ChatView component to your SwiftUI view with minimal required configuration. This basic setup provides full chat functionality with real-time messaging.

Required Parameters:

userJWT String required

JWT authentication token required to initialize the UI SDK and authenticate the user.

channelId String required

The unique identifier of the chat channel to display.

supportedLanguage SupportedLanguage required

Language for localizing SDK content (e.g., .english, .spanish).

info

The userJWT, channelId, and supportedLanguage are minimum required parameters. All other parameters are optional and default to nil. See Configuration Parameters for additional options.

***ContentView.swift:***
swift
import SwiftUI
import VirtualStadiumSDK

struct ContentView: View {
    let userJWT = "<your-jwt-token>"
    let channelId = "<your-channel-id>"
    
    var body: some View {
        ChatView(
            userJWT: userJWT,
            channelId: channelId,
            matchStatusManager: nil,
            flashBetManager: nil,
            insightsManager: nil,
            supportedLanguage: .english,
            onBetShareHandler: nil,
            onCopyToBetslip: nil,
            onTagSelected: nil,
            onReachSupportTeam: nil,
            onOpenProfile: nil,
            analyticsProvider: nil,
            onError: nil,
            chatSettings: ChatSettings()
        )
        .navigationTitle("Chat")
    }
}

#Configuration Parameters

The ChatView component supports various configuration options to customize appearance, behavior, and functionality.

#Required Parameters


userJWT String required

JWT authentication token required to initialize the UI SDK and authenticate the user.


channelId String required

The unique identifier of the chat channel to display.


supportedLanguage SupportedLanguage required

Language code for localizing SDK content and fetching localized feeds. Available options: .english, .spanish, .portuguese, etc.


#Optional Parameters


matchStatusManager MatchStatusManager? optional

Manager for controlling match status behavior. Default is nil. See Match Status.


flashBetManager FlashBetManager? optional

Manager for handling Flash Bet events and interactions. Default is nil. See Flash Bet.


insightsManager InsightsManager? optional

Manager for handling Bet insights functionality. Default is nil. See Bet Insights.


onBetShareHandler ((@escaping ([BetPayload]) -> Void) -> Void)? optional

Callback invoked when the user wants to share a bet. Receives a completion handler that should be called with the user's bet slips. If nil, the "Share Bet" button is hidden.


onCopyToBetslip ((BetPayload) -> Void)? optional

Callback invoked when a user copies a bet from the chat. Returns the BetPayload to add to your bet slip.


onTagSelected ((Tag) -> Void)? optional

Callback invoked when a user clicks a tag in a message.


onReachSupportTeam ((InfractionReason) -> Void)? optional

Callback for the "Contact Support" button shown in error/infraction screens. If nil, the button is hidden.


onOpenProfile ((String) -> Void)? optional

Callback invoked when a user clicks a profile. Receives the user ID.


analyticsProvider AnalyticsProvider? optional

Provider for logging analytics events throughout the SDK. Default is nil.


onError ((Error) -> Void)? optional

Callback for handling SDK errors. Receives error details for logging or displaying to users.


chatSettings ChatSettings optional

Customization settings for theming, fonts, icons, and UI behavior. Default is ChatSettings(). See Chat Settings.


#Flash Bet

Flash Bet enables time-sensitive quick betting triggered by live match events. Users receive real-time betting opportunities with a countdown timer.

#How It Works

  1. Event Trigger - A flash bet event is triggered from the moderation system
  2. Market Update - Your app provides market data within 60 seconds (configurable)
  3. User Selection - Users have limited time to select an outcome
  4. Callback - When an outcome is selected, your callback is invoked

#FlashBetManager API

swift
public class FlashBetManager: ObservableObject {
    // Callback triggered when a flash bet event is received
    public var onFlashBetEventReceived: ((ReceivedEvent) -> Void)?
    
    // Callback triggered when user selects an outcome
    public var onFlashBetOutcomeSelected: ((EventOutcome) -> Void)?
    
    // Publisher for updating flash bet market status
    public var updateFlashBetEventPublisher: PassthroughSubject<FlashBetEventStatus, Never>?
    
    public init()
    public func clear()
}
warning

Timeout Behavior If you don't provide a market within the configured timeout (default 60 seconds) of the flash bet trigger, the flash bet will automatically be canceled and move to the next event in the queue (if any).

The timeout duration can be configured via ChatSettings.generalSettings.flashBetTimeoutDuration.

Market suspension timeout can be configured in the moderation panel or via the moderation API.

#Market Data Structure

swift
public struct FlashBetEventStatus {
    public let flashBetEvent: ReceivedEvent
    public let market: Market?
    
    public init(flashBetEvent: ReceivedEvent, market: Market? = nil)
}

public struct Market {
    let id: String
    let name: String
    let status: MarketStatus
    let outcomes: [Outcome]
}

public enum MarketStatus {
    case active      // Market is active and accepting bets
    case suspended   // Market is suspended, timer pauses
    case deactivated // Market is deactivated, outcomes disabled
}

public struct Outcome {
    let id: String
    let name: String
    let odds: Double
    let status: OutcomeStatus
}

public enum OutcomeStatus {
    case active
    case deactivated
    case suspended
}

#Implementation

Flash Bet Implementation:

swift
import SwiftUI
import VirtualStadiumSDK

struct ChatWithFlashBet: View {
    let jwt: String
    let channelId: String
    
    @StateObject private var flashBetManager = FlashBetManager()
    
    var body: some View {
        NavigationView {
            ChatView(
                userJWT: jwt,
                channelId: channelId,
                flashBetManager: flashBetManager,
                supportedLanguage: .english,
            )
            .navigationTitle("Chat")
        }
        .onAppear {
            setupFlashBet()
        }
    }
    
    private func setupFlashBet() {
        // Handle flash bet events
        flashBetManager.onFlashBetEventReceived = { receivedEvent in
            print("Flash bet event received: \(receivedEvent.id)")
            
            // Fetch market data from your backend
            Task {
                do {
                    /**
                     * TODO(developer): Replace with your actual market fetching logic
                     */
                    let market = try await fetchFlashBetMarket(
                        eventId: receivedEvent.id
                    )
                    
                    // Update the flash bet with market data
                    let status = FlashBetEventStatus(
                        flashBetEvent: receivedEvent,
                        market: market
                    )
                    flashBetManager.updateFlashBetEventPublisher?.send(status)
                    
                } catch {
                    print("Failed to fetch flash bet market: \(error)")
                }
            }
        }
        
        // Handle outcome selection
        flashBetManager.onFlashBetOutcomeSelected = { outcome in
            // Place the bet in your betting system
            placeBet(outcome: outcome)
            print("User selected outcome: \(outcome.name)")
        }
    }
    
    private func fetchFlashBetMarket(eventId: String) async throws -> Market {
        /**
         * TODO(developer): Replace with your actual API call
         */
        // Example: Fetch from your backend
        let response = try await apiClient.getFlashBetMarket(eventId: eventId)
        
        return Market(
            id: response.id,
            name: response.name,
            status: .active,
            outcomes: response.outcomes.map { outcome in
                Outcome(
                    id: outcome.id,
                    name: outcome.name,
                    odds: outcome.odds,
                    status: .active
                )
            }
        )
    }
    
    private func placeBet(outcome: EventOutcome) {
        /**
         * TODO(developer): Implement your bet placement logic
         */
        print("Placing bet on outcome: \(outcome.name)")
        // Add to bet slip or place bet immediately
    }
}

#Market Status Management

Use market status to control flash bet behavior during different match scenarios. This allows you to pause, resume, or deactivate markets based on live events.

Market Status Control:

swift
// Suspend the market (e.g., due to a goal)
let suspendedStatus = FlashBetEventStatus(
    flashBetEvent: currentEvent,
    market: currentMarket.with(status: .suspended)
)
flashBetManager.updateFlashBetEventPublisher?.send(suspendedStatus)

// Resume the market
let activeStatus = FlashBetEventStatus(
    flashBetEvent: currentEvent,
    market: currentMarket.with(status: .active)
)
flashBetManager.updateFlashBetEventPublisher?.send(activeStatus)

// Deactivate the market
let deactivatedStatus = FlashBetEventStatus(
    flashBetEvent: currentEvent,
    market: currentMarket.with(status: .deactivated)
)
flashBetManager.updateFlashBetEventPublisher?.send(deactivatedStatus)

#Bet Insights

Bet Insights provides AI-powered betting suggestions based on Sportradar's real-time data feeds. The feature displays the most relevant markets and outcomes for the current match.

#How It Works

  1. Match Configuration - Set a matchId via the moderation panel or API
  2. Automatic Fetching - SDK fetches insights based on match state:
    • Pre-match: Longer refresh intervals (far future matches)
    • Live: ~10 second refresh intervals
    • Post-match: Fetching stops, insights cleared
  3. Outcome Mapping - Your app maps SDK outcomes to your betting data
  4. Display - SDK's algorithm selects and displays the most relevant insights
info

Important Notes

  • The onNewInsightsReceived callback returns all available markets and outcomes from the feed
  • The SDK's algorithm selects which insights to display
  • Not all provided outcomes will necessarily appear on screen
  • Market and outcome IDs must match SDK identifiers for proper mapping
  • Set useClientData = true in OutcomesPayload to use your custom market/outcome names

#InsightsManager API

swift
public class InsightsManager: ObservableObject {
    // Callback triggered when new insights are received
    public var onNewInsightsReceived: (([OutcomePayloadRequest]) -> Void)?
    
    // Callback triggered when user clicks an insight outcome
    public var onInsightOutcomeSelected: ((_ marketId: String, _ outcomeId: String) -> Void)?
    
    // Publisher for sending updated insights to display
    public var onOutcomesGenerated: PassthroughSubject<OutcomesPayload, Never>?
    
    public init()
    public func clear()
}

#Client Data Vs Feed Data

The useClientData flag in OutcomesPayload determines naming sources:

  • useClientData = true - Use your custom market and outcome names
  • useClientData = false - Use names from Sportradar's feed

Example:

  • Feed data: Market "1x2", Outcome "PSG"
  • Your data: Market "Match Winner", Outcome "Paris Saint Germain"

#Data Structures

#OutcomePayloadRequest

The SDK provides this data for you to map to your betting system:

swift
/**
 * Request structure from the SDK insights feed
 */
public struct OutcomePayloadRequest: Codable {
    public var eventId: String
    public var marketId: String
    public var outcomeId: String
    public var specifier: SpecifierRequest?
    
    public init(
        eventId: String,
        marketId: String,
        outcomeId: String,
        specifier: SpecifierRequest?
    )
}

public struct SpecifierRequest: Codable {
    public var name: String   // e.g., "Total Goals"
    public var value: String  // e.g., "Over 2.5"
    
    public init(name: String, value: String)
}

#OutcomesPayload

Your app builds this structure to provide insights data back to the SDK:

swift
/**
 * Payload you provide to the SDK
 */
public struct OutcomesPayload: Codable {
    public var outcomes: [ClientInsightData] = []
    public var useClientData: Bool = false
    
    public init(
        outcomes: [ClientInsightData] = [],
        useClientData: Bool = false
    )
}

public struct ClientInsightData: Codable {
    public var event: InsightEvent
    public var market: InsightMarket
    public var outcome: InsightOutcome?
    
    public init(
        event: InsightEvent,
        market: InsightMarket,
        outcome: InsightOutcome? = nil
    )
}

swift
public struct InsightEvent: Codable {
    public var id: String
    public var teams: [InsightTeam]
    public var isLive: Bool
    public var sport: InsightSport
}

public struct InsightTeam: Codable {
    public var id: String?
    public var name: String
}

public struct InsightSport: Codable {
    public var id: String?
    public var name: String
}

public struct InsightMarket: Codable {
    public var id: String
    public var name: String
    public var status: InsightStatus
    public var specifier: InsightSpecifier?
}

public struct InsightSpecifier: Codable {
    public var value: String?
}
swift
public struct InsightOutcome: Codable {
    public var id: String
    public var name: String
    public var status: InsightStatus
    public var competitor: String?
    public var oddsDecimal: Double
    public var odds: String
    public var isSelected: Bool
}

public enum InsightStatus: Codable {
    case active       // Available for betting
    case deactivated  // Temporarily deactivated
    case suspended    // Temporarily suspended
}

#Implementation

Bet Insights Implementation:

swift
import SwiftUI
import VirtualStadiumSDK
import Combine

struct ChatWithInsights: View {
    let jwt: String
    let channelId: String
    
    @StateObject private var insightsManager = InsightsManager()
    @State private var cancellables = Set<AnyCancellable>()
    
    var body: some View {
        ChatView(
            userJWT: jwt,
            channelId: channelId,
            insightsManager: insightsManager,
            supportedLanguage: .english
        )
        .onAppear {
            setupInsights()
        }
    }
    
    private func setupInsights() {
        // Handle new insights data
        insightsManager.onNewInsightsReceived = { outcomeRequests in
            Task {
                /**
                 * TODO(developer): Replace with your actual market data fetching logic
                 */
                // Map SDK requests to your betting data
                let clientData = try await mapInsightsData(outcomeRequests)
                
                // Update insights display
                let payload = OutcomesPayload(
                    outcomes: clientData,
                    useClientData: true  // Use your custom names
                )
                
                insightsManager.onOutcomesGenerated?.send(payload)
            }
        }
        
        // Handle outcome selection
        insightsManager.onInsightOutcomeSelected = { marketId, outcomeId in
            // Add to bet slip or navigate to betting screen
            addToBetSlip(marketId: marketId, outcomeId: outcomeId)
            print("Insight selected - Market: \(marketId), Outcome: \(outcomeId)")
        }
    }
    
    private func mapInsightsData(_ requests: [OutcomePayloadRequest]) async throws -> [ClientInsightData] {
        /**
         * TODO(developer): Replace with your actual API call
         */
        var clientData: [ClientInsightData] = []
        
        for request in requests {
            // Fetch your market data
            let marketData = try await apiClient.getMarket(
                eventId: request.eventId,
                marketId: request.marketId,
                outcomeId: request.outcomeId
            )
            
            let insight = ClientInsightData(
                event: InsightEvent(
                    id: request.eventId,
                    teams: [
                        InsightTeam(id: "team1", name: marketData.homeTeam),
                        InsightTeam(id: "team2", name: marketData.awayTeam)
                    ],
                    isLive: marketData.isLive,
                    sport: InsightSport(id: "1", name: "Soccer")
                ),
                market: InsightMarket(
                    id: request.marketId,
                    name: marketData.marketName,
                    status: .active,
                    specifier: request.specifier.map { spec in
                        InsightSpecifier(value: spec.value)
                    }
                ),
                outcome: InsightOutcome(
                    id: request.outcomeId,
                    name: marketData.outcomeName,
                    status: .active,
                    competitor: nil,
                    oddsDecimal: marketData.odds,
                    odds: String(format: "%.2f", marketData.odds),
                    isSelected: false
                )
            )
            
            clientData.append(insight)
        }
        
        return clientData
    }
    
    private func addToBetSlip(marketId: String, outcomeId: String) {
        /**
         * TODO(developer): Implement your bet slip logic
         */
        print("Adding to bet slip - Market: \(marketId), Outcome: \(outcomeId)")
        // Add to your bet slip system
    }
}

#Testing With Mock Data

Enable mock insights for testing without a real match. This is useful during development to test the UI and interaction flows.

swift
let chatSettings = ChatSettings(
    generalSettings: GeneralSettings(
        useTestInsights: true
    )
)

ChatView(
    userJWT: jwt,
    channelId: channelId,
    insightsManager: insightsManager,
    supportedLanguage: .english,
    chatSettings: chatSettings
)

Use a mock data provider to generate test insights: MockInsightsProvider.swift:

swift
struct MockInsightsProvider {
    static func getMockInsights(
        from requests: [OutcomePayloadRequest],
        useRandomOdds: Bool = false
    ) -> OutcomesPayload {
        let clientData = requests.enumerated().map { index, request in
            let baseOdds = useRandomOdds ? Double.random(in: 1.4...3.0) : 2.5
            
            return ClientInsightData(
                event: InsightEvent(
                    id: request.eventId,
                    teams: [
                        InsightTeam(id: nil, name: "Team A"),
                        InsightTeam(id: nil, name: "Team B")
                    ],
                    isLive: true,
                    sport: InsightSport(id: "1", name: "Soccer")
                ),
                market: InsightMarket(
                    id: request.marketId,
                    name: "Market \(index + 1)",
                    status: .active,
                    specifier: request.specifier.map { spec in
                        InsightSpecifier(value: spec.value)
                    }
                ),
                outcome: InsightOutcome(
                    id: request.outcomeId,
                    name: "Outcome \(index + 1)",
                    status: .active,
                    competitor: nil,
                    oddsDecimal: baseOdds + Double(index),
                    odds: String(format: "%.2f", baseOdds + Double(index)),
                    isSelected: false
                )
            )
        }
        
        return OutcomesPayload(
            outcomes: clientData,
            useClientData: true
        )
    }
}

// Usage
insightsManager.onNewInsightsReceived = { requests in
    let mockPayload = MockInsightsProvider.getMockInsights(
        from: requests,
        useRandomOdds: true
    )
    insightsManager.onOutcomesGenerated?.send(mockPayload)
}

#Bet Sharing

Enable users to share their bet slips and copy bets from other community members. This creates a social betting experience where users can learn from and engage with each other's betting strategies.

#Implementation

tip

Hiding Share Button If onBetShareHandler is nil, the "Share Bet" button will be automatically hidden from the UI.

Bet Sharing Example:

swift
ChatView(
    userJWT: jwt,
    channelId: channelId,
    supportedLanguage: .english,
    
    // Provide user's bet slips for sharing
    onBetShareHandler: { completion in
        /**
         * TODO(developer): Replace with your bet slip fetching logic
         */
        Task {
            // Fetch from your bet slip system
            let betSlips = await getBetSlipsFromDatabase()
            completion(betSlips)
        }
    },
    
    // Handle copying a bet
    onCopyToBetslip: { betPayload in
        // Add to user's bet slip
        addToBetSlip(betPayload)
        
        // Show confirmation
        print("Bet copied to slip!")
    },
    
    chatSettings: ChatSettings()
)

#BetPayload Structure

swift
public struct BetPayload {
    let id: String
    let betSlipId: String
    let betType: BetType
    let currency: String
    let combinedOdds: Odds
    let stake: Stake?
    let payout: Payout?
    let cashOut: CashOut?
    let bets: [Bet]
}

public enum BetType {
    case betBuilder
    case multiBet
    case sameGameMulti
    case single
    case none
}

public struct Odds {
    let decimalValue: Float
    let displayValue: String?
}
swift
public struct Bet {
    let id: String
    let betType: BetType
    let event: BetEvent
    let markets: [BetMarket]
    let odds: Odds?
}

public struct BetEvent {
    let id: String
    let name: String?
    let teams: [Team]
}

public struct BetMarket {
    let id: String
    let name: String
    let outcomes: [Outcome]
}

public struct Outcome {
    let id: String
    let name: String
    let odds: Odds?
}

#Match Status

Control how match status affects theming for Flash Bets and Insights. The match status determines visual styling to indicate whether a match is live, pre-match, or if automatic detection is disabled.

#MatchStatusBehavior

swift
public enum MatchStatusBehavior {
    case live       // Force live theming
    case automatic  // Automatic based on feed (default)
    case disabled   // Disable automatic status changes
}

#MatchStatusManager API

swift
public class MatchStatusManager: ObservableObject {
    public var matchStatus: CurrentValueSubject<MatchStatusBehavior, Never>?
    
    public init()
    public func clear()
}

#Implementation

Match Status Control:

swift
import SwiftUI
import VirtualStadiumSDK
import Combine

struct ChatWithMatchStatus: View {
    let jwt: String
    let channelId: String
    
    @StateObject private var matchStatusManager = MatchStatusManager()
    
    var body: some View {
        ChatView(
            userJWT: jwt,
            channelId: channelId,
            matchStatusManager: matchStatusManager,
            supportedLanguage: .english
        )
        .onAppear {
            // Set match status behavior
            matchStatusManager.matchStatus?.send(.live)
        }
    }
}

// Update match status dynamically
matchStatusManager.matchStatus?.send(.automatic)
matchStatusManager.matchStatus?.send(.disabled)

#Chat Settings

Customize the appearance and behavior of the Chat component through comprehensive settings options.

#ChatSettings Structure

swift
public struct ChatSettings {
***Basic ChatSettings Usage:***

```swift
let chatSettings = ChatSettings(
    generalSettings: GeneralSettings(
        oddsType: .us,
        presentedInBottomSheet: false,
        flashBetTimeoutDuration: 60.0,
        useTestInsights: false
    ),
    themeSettings: ThemeSettings(
        mainColorPaletteTheme: MainColorPaletteTheme()
    )
)

ChatView(
    userJWT: jwt,
    channelId: channelId,
    supportedLanguage: .english,
    chatSettings: chatSettings
)

#GeneralSettings

Configure general behavior including odds format, presentation style, and feature flags.

swift
public struct GeneralSettings {
    public var oddsType: OddsType
    public var presentedInBottomSheet: Bool
    public var flashBetTimeoutDuration: Double
    public var useTestInsights: Bool
    
    public init(
        oddsType: OddsType = .eu,
        presentedInBottomSheet: Bool = false,
        flashBetTimeoutDuration: Double = 60.0,
        useTestInsights: Bool = false
    )
}

public enum OddsType: String {
    case us = "us"  // American format (+150, -200)
    case uk = "uk"  // Fractional format (3/2, 1/5)
    case eu = "eu"  // Decimal format (2.50, 1.20)
}

GeneralSettings Example:

swift
let generalSettings = GeneralSettings(
    oddsType: .us,                      // American odds format
    presentedInBottomSheet: true,        // Optimize for bottom sheet
    flashBetTimeoutDuration: 90.0,      // 90 second timeout
    useTestInsights: false               // Use real insights
)

let chatSettings = ChatSettings(
    generalSettings: generalSettings
)

#ThemeSettings

Control all color aspects of the UI including general colors, Flash Bet theming, and Insights theming. You can customize colors for both pre-match and live states.

swift
public struct ThemeSettings {
    public var mainColorPaletteTheme: MainColorPaletteTheme
    public var textColorTheme: TextColorTheme
    public var chatViewTheme: ChatViewTheme
    public var reactionsTheme: ReactionsTheme
    public var errorAndInfoMessageTheme: ErrorAndInfoMessageTheme
    public var messageInputTheme: MessageInputTheme
    public var tabsAndNavigationTheme: TabsAndNavigationTheme
    public var betShareTheme: BetShareTheme
    public var chatMessagesTheme: ChatMessagesTheme
    public var replyAndReportMessageTheme: ReplyAndReportMessageTheme
    public var betSlipTheme: BetSlipTheme
    public var profileTheme: ProfileTheme
    public var lightInsightsTheme: LightInsightsTheme
    public var darkInsightsTheme: DarkInsightsTheme
    public var lightFlashBetTheme: LightFlashBetTheme
    public var darkFlashBetTheme: DarkFlashBetTheme
}

Custom Theme Example:

swift
import SwiftUI

let customTheme = ThemeSettings(
    mainColorPaletteTheme: MainColorPaletteTheme(
        primaryColor: Color(hex: "1976D2"),
        secondaryColor: Color(hex: "FF6F00"),
        tertiaryColor: Color(hex: "4CAF50")
    ),
    chatViewTheme: ChatViewTheme(
        scrollToBottomBackgroundColor: Color(hex: "1976D2").opacity(0.15),
        loadingIndicatorColor: Color(hex: "1976D2")
    ),
    messageInputTheme: MessageInputTheme(
        sendMessageIconEnabledColor: Color(hex: "1976D2")
    ),
    lightInsightsTheme: LightInsightsTheme(
        seenCountBadgeBackground: Color(hex: "D32F2F"),
        closeIconColor: Color(hex: "1976D2"),
        pagerIndicatorSelected: Color(hex: "1976D2"),
        pagerIndicatorUnselected: Color(hex: "1976D2").opacity(0.2)
    ),
    lightFlashBetTheme: LightFlashBetTheme(
        flashBetCloseIconBackground: Color.white.opacity(0.2),
        flashBetExpandIconBackground: Color.white.opacity(0.2),
        flashBetOutcomeEnabledBackground: Color.white.opacity(0.2)
    )
)

let chatSettings = ChatSettings(
    themeSettings: customTheme
)

#FontSettings

Set custom fonts to match your application's branding and typography style.

swift
public struct FontSettings {
    public let fontUltraLight: UIFont
    public let fontThin: UIFont
    public let fontLight: UIFont
    public let fontRegular: UIFont
    public let fontMedium: UIFont
    public let fontSemibold: UIFont
    public let fontBold: UIFont
    public let fontHeavy: UIFont
    public let fontBlack: UIFont
    public var inputFieldPlaceholderFont: UIFont
    
    public init(
        fontUltraLight: UIFont? = nil,
        fontThin: UIFont? = nil,
        fontLight: UIFont? = nil,
        fontRegular: UIFont? = nil,
        fontMedium: UIFont? = nil,
        fontSemibold: UIFont? = nil,
        fontBold: UIFont? = nil,
        fontHeavy: UIFont? = nil,
        fontBlack: UIFont? = nil,
        inputFieldPlaceholderFont: UIFont? = nil
    )
}

Custom Font Example:

swift
// Load custom font
guard let customFont = UIFont(name: "YourCustomFont-Regular", size: 16) else {
    fatalError("Failed to load custom font")
}

let fontSettings = FontSettings(
    fontRegular: customFont,
    fontMedium: UIFont(name: "YourCustomFont-Medium", size: 16),
    fontBold: UIFont(name: "YourCustomFont-Bold", size: 16),
    inputFieldPlaceholderFont: customFont
)

let chatSettings = ChatSettings(
    fontSettings: fontSettings
)

#IconSettings

Replace default icons with custom images to match your application's design system.

swift
public struct IconSettings {
***Custom Icons Example:***

```swift
let iconSettings = IconSettings(
    chatIcons: ChatIconSettings(
        sendIcon: UIImage(named: "custom_send"),
        attachIcon: UIImage(named: "custom_attach"),
        replyIcon: UIImage(named: "custom_reply")
    ),
    eventIcons: EventIconSettings(
        goalIcon: UIImage(named: "custom_goal"),
        cardIcon: UIImage(named: "custom_card")
    ),
    sportIcons: SportIconSettings(
        footballIcon: UIImage(named: "custom_football"),
        basketballIcon: UIImage(named: "custom_basketball")
    )
)

let chatSettings = ChatSettings(
    iconSettings: iconSettings
)

#TranslationSettings

Customize all text strings displayed in the SDK for complete localization control.

info

The TranslationSettings struct contains hundreds of customizable strings. See the full structure in the provided code for all available translations.

Custom Translations Example:

swift
let translationSettings = TranslationSettings(
    GeneralVip: "VIP",
    GeneralReply: "Responder",
    GeneralReport: "Reportar",
    GeneralSubmit: "Enviar",
    GeneralCancel: "Cancelar",
    MessageInputPlaceholder: "Escreva uma mensagem",
    BetViewStake: "Aposta",
    BetViewReturn: "Retorno",
    ChatEmptyTitle: "Seja o primeiro!",
    ChatEmptyDescription: "O que você está pensando?"
)

let chatSettings = ChatSettings(
    translationSettings: translationSettings
)

#Complete Example

Here's a comprehensive example with all features enabled:

ComprehensiveChatExample.swift:

swift
import SwiftUI
import VirtualStadiumSDK
import Combine

struct ComprehensiveChatView: View {
    let jwt: String
    let channelId: String
    
    @StateObject private var flashBetManager = FlashBetManager()
    @StateObject private var insightsManager = InsightsManager()
    @StateObject private var matchStatusManager = MatchStatusManager()
    
    @State private var showError = false
    @State private var errorMessage = ""
    @State private var showProfileSheet = false
    @State private var selectedUserId: String?
    
    var body: some View {
        NavigationView {
            ChatView(
                userJWT: jwt,
                channelId: channelId,
                matchStatusManager: matchStatusManager,
                flashBetManager: flashBetManager,
                insightsManager: insightsManager,
                supportedLanguage: .english,
                
                // Bet sharing
                onBetShareHandler: { completion in
                    Task {
                        let betSlips = await getBetSlips()
                        completion(betSlips)
                    }
                },
                
                onCopyToBetslip: { betPayload in
                    copyBetToSlip(betPayload)
                },
                
                // User interactions
                onTagSelected: { tag in
                    handleTagSelection(tag)
                },
                
                onReachSupportTeam: { reason in
                    openSupport(reason: reason)
                },
                
                onOpenProfile: { userId in
                    selectedUserId = userId
                    showProfileSheet = true
                },
                
                // Analytics
                analyticsProvider: createAnalyticsProvider(),
                
                // Error handling
                onError: { error in
                    errorMessage = error.localizedDescription
                    showError = true
                },
                
                // Customization
                chatSettings: createCustomSettings()
            )
            .navigationTitle("Chat")
        }
        .sheet(isPresented: $showProfileSheet) {
            if let userId = selectedUserId {
                ProfileView(userId: userId)
            }
        }
        .alert("Error", isPresented: $showError) {
            Button("OK", role: .cancel) { }
        } message: {
            Text(errorMessage)
        }
        .onAppear {
            setupManagers()
        }
    }
    
    private func setupManagers() {
        // Flash Bet setup
        flashBetManager.onFlashBetEventReceived = { event in
            Task {
                let market = try? await fetchFlashBetMarket(eventId: event.id)
                let status = FlashBetEventStatus(flashBetEvent: event, market: market)
                flashBetManager.updateFlashBetEventPublisher?.send(status)
            }
        }
        
        flashBetManager.onFlashBetOutcomeSelected = { outcome in
            placeBet(outcome: outcome)
        }
        
        // Insights setup
        insightsManager.onNewInsightsReceived = { requests in
            Task {
                let clientData = try? await mapInsightsData(requests)
                let payload = OutcomesPayload(
                    outcomes: clientData ?? [],
                    useClientData: true
                )
                insightsManager.onOutcomesGenerated?.send(payload)
            }
        }
        
        insightsManager.onInsightOutcomeSelected = { marketId, outcomeId in
            addToBetSlip(marketId: marketId, outcomeId: outcomeId)
        }
        
        // Match status setup
        matchStatusManager.matchStatus?.send(.automatic)
    }
    
    private func createAnalyticsProvider() -> AnalyticsProvider {
        return AnalyticsProvider(
            onEventReceived: { eventType in
                // Handle generic analytics events
                print("Analytics event: \(eventType)"
            },
            onBetEventReceived: { eventType, betPayload, betBuilderId in
                // Handle bet-related analytics events
                print("Bet analytics event: \(eventType)")
                print("Bet ID: \(betPayload.id), Type: \(betPayload.betType)")
            },
            onReactionEventReceived: { eventType, reaction in
                // Handle reaction analytics events
                print("Reaction analytics event: \(eventType)")
                print("Reaction: \(reaction)")
            },
            onMessageSent: { eventType, messageType in
                // Handle message sent analytics events
                print("Message sent analytics event: \(eventType)")
                print("Message type: \(messageType.rawValue)")
            }
        )
    }
    
    private func createCustomSettings() -> ChatSettings {
        let customTheme = ThemeSettings(
            mainColorPaletteTheme: MainColorPaletteTheme(
                primaryColor: Color.blue,
                secondaryColor: Color.orange
            )
        )
        
        let customFont = FontSettings(
            fontRegular: UIFont(name: "CustomFont-Regular", size: 16)
        )
        
        return ChatSettings(
            generalSettings: GeneralSettings(
                oddsType: .us,
                presentedInBottomSheet: false,
                flashBetTimeoutDuration: 60.0,
                useTestInsights: false
            ),
            themeSettings: customTheme,
            fontSettings: customFont
        )
    }
    
    // Helper methods
    private func getBetSlips() async -> [BetPayload] {
        /**
         * TODO(developer): Fetch bet slips from your backend
         */
        // Example implementation:
        // let betSlips = try? await apiClient.fetchUserBetSlips()
        // return betSlips ?? []
        return []
    }
    
    private func copyBetToSlip(_ bet: BetPayload) {
        /**
         * TODO(developer): Add bet to user's bet slip
         */
        print("Copying bet to slip: \(bet.id)")
        // Example: betSlipManager.addBet(bet)
    }
    
    private func handleTagSelection(_ tag: Tag) {
        /**
         * TODO(developer): Handle tag selection
         */
        print("Tag selected: \(tag.name)")
    }
    
    private func openSupport(reason: InfractionReason?) {
        /**
         * TODO(developer): Open support interface
         */
        print("Opening support. Reason: \(reason?.description ?? "None")")
    }
    
    private func fetchFlashBetMarket(eventId: String) async throws -> Market {
        /**
         * TODO(developer): Fetch flash bet market from your backend
         */
        // Example implementation:
        // let response = try await apiClient.getFlashBetMarket(eventId: eventId)
        // return Market(
        //     id: response.id,
        //     name: response.name,
        //     status: .active,
        //     outcomes: response.outcomes
        // )
        
        // Mock implementation for example
        return Market(
            id: "market_123",
            name: "Next Goal Scorer",
            status: .active,
            outcomes: [
                Outcome(id: "outcome_1", name: "Player A", odds: 2.5, status: .active),
                Outcome(id: "outcome_2", name: "Player B", odds: 3.2, status: .active)
            ]
        )
    }
    
    private func placeBet(outcome: EventOutcome) {
        /**
         * TODO(developer): Place bet on selected outcome
         */
        print("Placing bet on outcome: \(outcome.name)")
    }
    
    private func mapInsightsData(_ requests: [OutcomePayloadRequest]) async throws -> [ClientInsightData] {
        /**
         * TODO(developer): Map SDK insights to your betting data
         */
        // Example implementation:
        var clientData: [ClientInsightData] = []
        
        for request in requests {
            // Fetch market data from your backend
            // let marketData = try await apiClient.getMarket(
            //     eventId: request.eventId,
            //     marketId: request.marketId,
            //     outcomeId: request.outcomeId
            // )
            
            // Mock data for example
            let insight = ClientInsightData(
                event: InsightEvent(
                    id: request.eventId,
                    teams: [
                        InsightTeam(id: "team1", name: "Team A"),
                        InsightTeam(id: "team2", name: "Team B")
                    ],
                    isLive: true,
                    sport: InsightSport(id: "1", name: "Soccer")
                ),
                market: InsightMarket(
                    id: request.marketId,
                    name: "Match Winner",
                    status: .active,
                    specifier: request.specifier.map { InsightSpecifier(value: $0.value) }
                ),
                outcome: InsightOutcome(
                    id: request.outcomeId,
                    name: "Team A",
                    status: .active,
                    competitor: nil,
                    oddsDecimal: 2.5,
                    odds: "2.50",
                    isSelected: false
                )
            )
            
            clientData.append(insight)
        }
        
        return clientData
    }
    
    private func addToBetSlip(marketId: String, outcomeId: String) {
        /**
         * TODO(developer): Add insight outcome to bet slip
         */
        print("Adding to bet slip - Market: \(marketId), Outcome: \(outcomeId)")
    }
}

// Mock ProfileView for example
struct ProfileView: View {
    let userId: String
    
    var body: some View {
        NavigationView {
            VStack {
                Text("Profile")
                    .font(.largeTitle)
                Text("User ID: \(userId)")
                    .foregroundColor(.secondary)
            }
            .navigationTitle("User Profile")
            .navigationBarTitleDisplayMode(.inline)
        }
    }
}
Last updated 13 days ago
Is this site helpful?
Virtual Stadium, Moderation, Engagement Tools
ComponentsProfile Component
On this page
  • Overview
  • Basic Integration
  • SwiftUI
  • Configuration Parameters
  • Required Parameters
  • Optional Parameters
  • Flash Bet
  • How It Works
  • FlashBetManager API
  • Market Data Structure
  • Implementation
  • Market Status Management
  • Bet Insights
  • How It Works
  • InsightsManager API
  • Client Data Vs Feed Data
  • Data Structures
  • Implementation
  • Testing With Mock Data
  • Bet Sharing
  • Implementation
  • BetPayload Structure
  • Match Status
  • MatchStatusBehavior
  • MatchStatusManager API
  • Implementation
  • Chat Settings
  • ChatSettings Structure
  • GeneralSettings
  • ThemeSettings
  • FontSettings
  • IconSettings
  • TranslationSettings
  • Complete Example