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.
To integrate the Chat component:
This guide will walk you through each step with code examples and best practices.
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).
The userJWT, channelId, and supportedLanguage are minimum required parameters. All other parameters are optional and default to nil. See Configuration Parameters for additional options.
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")
}
}The ChatView component supports various configuration options to customize appearance, behavior, and functionality.
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.
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 enables time-sensitive quick betting triggered by live match events. Users receive real-time betting opportunities with a countdown timer.
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()
}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.
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
}Flash Bet Implementation:
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
}
}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:
// 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 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.
matchId via the moderation panel or APIImportant Notes
onNewInsightsReceived callback returns all available markets and outcomes from the feeduseClientData = true in OutcomesPayload to use your custom market/outcome namespublic 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()
}The useClientData flag in OutcomesPayload determines naming sources:
useClientData = true - Use your custom market and outcome namesuseClientData = false - Use names from Sportradar's feedExample:
The SDK provides this data for you to map to your betting system:
/**
* 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)
}Your app builds this structure to provide insights data back to the SDK:
/**
* 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
)
}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?
}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
}Bet Insights Implementation:
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
}
}Enable mock insights for testing without a real match. This is useful during development to test the UI and interaction flows.
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:
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)
}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.
Hiding Share Button
If onBetShareHandler is nil, the "Share Bet" button will be automatically hidden from the UI.
Bet Sharing Example:
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()
)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?
}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?
}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.
public enum MatchStatusBehavior {
case live // Force live theming
case automatic // Automatic based on feed (default)
case disabled // Disable automatic status changes
}public class MatchStatusManager: ObservableObject {
public var matchStatus: CurrentValueSubject<MatchStatusBehavior, Never>?
public init()
public func clear()
}Match Status Control:
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)Customize the appearance and behavior of the Chat component through comprehensive settings options.
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
)Configure general behavior including odds format, presentation style, and feature flags.
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:
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
)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.
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:
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
)Set custom fonts to match your application's branding and typography style.
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:
// 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
)Replace default icons with custom images to match your application's design system.
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
)Customize all text strings displayed in the SDK for complete localization control.
The TranslationSettings struct contains hundreds of customizable strings. See the full structure in the provided code for all available translations.
Custom Translations Example:
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
)Here's a comprehensive example with all features enabled:
ComprehensiveChatExample.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)
}
}
}