The Central Hub component provides a comprehensive social betting platform where users can share bets, interact with the community, manage their profile, view notifications, and engage with other users. It serves as the central social hub for the Virtual Stadium experience.
This guide will walk you through each step with code examples and best practices.
Managers.SDK.shared.initialize()The Central Hub component requires JWT authentication and a user ID. It can be integrated as a standalone screen or as part of your navigation flow.
Required Parameters:
userJWT String required
JWT authentication token required to initialize the SDK and authenticate the user.
userId String required
The unique identifier of the logged-in user. This is used to personalize the Central Hub experience and distinguish between the user's own profile and other users' profiles.
onCopyToBetslip @escaping ((BetPayload) -> Void) required
Callback invoked when a user copies a bet from another user. Returns the BetPayload to add to your bet slip.
onBetShareHandler @escaping ((([BetPayload]) -> Void) -> Void) required
Callback invoked when the user wants to share a bet. Receives a completion handler that should be called with the user's bet slips.
onClose @escaping (() -> Void) required
Callback invoked when the user navigates back or closes the Central Hub. Use this to handle navigation back to your app.
Optional Parameters:
centralHubSettings CentralHubSettings optional
Customization settings for theming, fonts, translations, and odds display. Default: CentralHubSettings()
The Central Hub handles its own navigation internally, including profile screens, bet sharing, search, followers, and notifications.
import SwiftUI
import VirtualStadiumUISDK
struct ContentView: View {
let userJWT = "<your-jwt-token>"
let userId = "<your-user-id>"
@State private var showCentralHub = true
var body: some View {
if showCentralHub {
CentralHubView(
userJWT: userJWT,
userId: userId,
centralHubSettings: CentralHubSettings(),
onCopyToBetslip: { betPayload in
// Handle copying bet to user's bet slip
addBetToSlip(betPayload)
},
onBetShareHandler: { completion in
// Return user's bet slips for sharing
Task {
let betSlips = await getUserBetSlips()
completion(betSlips)
}
},
onClose: {
// Handle back navigation
showCentralHub = false
}
)
}
}
private func getUserBetSlips() async -> [BetPayload] {
/**
* TODO(developer): Implement your bet slip fetching logic
*/
return []
}
private func addBetToSlip(_ betPayload: BetPayload) {
/**
* TODO(developer): Implement your bet slip logic
*/
print("Bet copied: \(betPayload.id)")
}
}The Central Hub component supports various configuration options to customize appearance, behavior, and functionality.
userJWT String required
JWT authentication token required to initialize the SDK and authenticate the user.
userId String required
The unique identifier of the logged-in user. This is used to personalize the Central Hub experience and distinguish between the user's own profile and other users' profiles.
onCopyToBetslip @escaping ((BetPayload) -> Void) required
Callback invoked when a user copies a bet from another user's shared bets. Returns the BetPayload to add to your bet slip.
BetPayload Structure:
public struct BetPayload {
let id: String
let betType: BetType // Single, Multi, Bet Builder, etc.
let selections: [Selection]
let stake: Double
let totalOdds: Double
let potentialWin: Double
// ... additional bet details
}onBetShareHandler @escaping ((([BetPayload]) -> Void) -> Void) required
Callback invoked when the user wants to share a bet with the community. This function receives a completion handler that should be called with the user's available bet slips.
The completion handler pattern allows asynchronous fetching of bet slips from your backend or local storage.
onClose @escaping (() -> Void) required
Callback invoked when the user navigates back or closes the Central Hub. Use this to handle navigation back to your app's main interface.
centralHubSettings CentralHubSettings optional
Customization settings for theming, fonts, translations, and UI behavior. Default is CentralHubSettings(). See Central Hub Settings.
The Central Hub provides a rich set of social betting features organized into distinct sections:
The profile section displays user information and activity:
| Feature | Description |
|---|---|
| User Header | Display name, avatar, follower/following counts |
| Profile Statistics | Bets shared, bets copied, engagement metrics |
| My Profile | When viewing own profile: edit profile, manage settings |
| Other Profiles | When viewing others: follow/unfollow, view shared bets |
| Bet Details | Each bet shows type, markets, odds, timestamp, reactions |
The bets section focuses on bet discovery and interaction:
| Feature | Description |
|---|---|
| Bet Feed | Community feed of shared bets from all users |
| Reactions | React to shared bets with various emojis |
| Comments | Comment on shared bets and engage in discussions |
| Copy Bets | One-tap copying of bets to your bet slip |
| Filter Options | Filter by bet type, time period, user relationships |
| Share Your Bets | Share your own bet slips with the community |
Search functionality to discover users and bets:
| Feature | Description |
|---|---|
| User Search | Search for users by username or display name |
| Profile Access | Quick access to user profiles from search results |
| Follow from Search | Follow/unfollow users directly from search |
| Filter Options | Filter by all users, following, or followers |
Manage social connections:
| Feature | Description |
|---|---|
| Followers List | View users who follow you |
| Following List | View users you follow |
| Follow Management | Follow/unfollow from the lists |
| Profile Navigation | Navigate to user profiles from lists |
| Sorting Options | Sort by latest or earliest connections |
Stay updated with community activity:
| Feature | Description |
|---|---|
| Bet Share Notifications | Notifications when users you follow share bets |
| Comment Notifications | Notifications for comments on your shared bets |
| Social Notifications | Notifications for new followers and social activity |
| Read/Unread States | Visual indicators for new notifications |
| Clear All | Option to clear all notifications at once |
Complete bet sharing experience:
First-time user experience:
The Central Hub manages its own internal navigation:
Central Hub Entry
├── Onboarding (first-time users only)
│
├── My Profile Tab
│ ├── Edit Profile
│ │ ├── Privacy Settings
│ │ └── Notification Preferences
│ ├── View Followers
│ ├── View Following
│ └── Notifications
│ ├── Betslip Notifications
│ ├── Social Notifications
│ └── Comment Notifications
│
├── Bets Tab
│ ├── Bet Details & Comments
│ ├── React to Bets
│ ├── Copy Bet
│ └── Share Bet Flow
│ ├── Bet Selection
│ ├── Privacy Options
│ └── Share Confirmation
│
├── Other User Profile
│ ├── Follow/Unfollow
│ ├── View Shared Bets
│ └── View Followers/Following
│
├── Search
│ ├── User Search
│ └── User Profiles
│
└── Commenting
├── View Comments
└── Add CommentAll navigation is handled internally by the Central Hub component. The only navigation callback is onClose, which is triggered when users want to exit the Central Hub.
The Central Hub can be extensively customized through the CentralHubSettings struct. This allows you to match the component's appearance to your app's design system.
public struct CentralHubSettings {
public var oddsType: OddsType
public var themeSettings: CentralHubThemeSettings
public var sportIcons: SportIconSettings
public var fontSettings: FontSettings
public var translationSettings: CentralHubTranslationSettings
}Configure how odds are displayed throughout the Central Hub:
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)
}Control all color aspects of the Central Hub through CentralHubThemeSettings:
Core Colors
Screen-Specific Colors
Component Colors
Toast Colors
Custom Settings Example:
import SwiftUI
import VirtualStadiumUISDK
let customSettings = CentralHubSettings(
// Odds format
oddsType: .us, // American odds
// Theme customization
themeSettings: CentralHubThemeSettings(
// Core background
background: Color(red: 0.95, green: 0.95, blue: 0.97),
// Toast colors
toastColors: ToastColors(
successColors: SuccessToastColors(
backgroundColor: .green,
textColor: .white,
iconColor: .white,
closeButtonColor: .white
),
errorColors: ErrorToastColors(
backgroundColor: .red,
textColor: .white,
iconColor: .white,
closeButtonColor: .white
)
),
// Card colors
cardColors: CentralHubCardsColors(
betShare: BetShareCardColors(
background: .white,
contentText: .black,
commentsIconColor: .blue,
commentsTextColor: .black
),
betSlip: BetSlipTheme(
betslipBackgroundColor: .white,
betslipBorderColor: Color.gray.opacity(0.2)
)
),
// Button colors
buttonColors: CentralHubButtonColors(
followButtonColors: FollowButtonColors(
background: Color.blue.opacity(0.15),
text: .blue
),
shareBetslipButtonColors: ShareBetslipButtonColors(
backgroundColor: .blue,
textColor: .white
)
),
// Navigation bar
navigationBarColors: CentralHubNavigationBarColors(
background: .white,
title: .black,
icons: .black
),
// Tab colors
tabColors: TabColors(
selectedTextColor: .black,
unselectedTextColor: .gray,
backgroundColor: Color.gray.opacity(0.2),
selectedBackgroundColor: .white,
tabContainerBackground: .white
),
// Onboarding
onboardingScreenColors: CentralHubOnboardingScreenColors(
backgroundColor: .white,
onboardingIcon: .blue,
welcomeTextColor: .black,
featureItem: FeatureItemColors(
featureIcon: .blue,
titleColor: .black,
descriptionColor: .gray
)
)
),
// Font customization
fontSettings: FontSettings(
fontRegular: UIFont(name: "CustomFont-Regular", size: 16),
fontMedium: UIFont(name: "CustomFont-Medium", size: 16),
fontBold: UIFont(name: "CustomFont-Bold", size: 16)
),
// Translation customization
translationSettings: CentralHubTranslationSettings(
// Customize all text strings
general: GeneralTranslations(
follow: "Follow",
unfollow: "Unfollow",
copyBetslip: "COPY BET"
)
)
)
// Use in CentralHubView
CentralHubView(
userJWT: jwt,
userId: userId,
centralHubSettings: customSettings,
onCopyToBetslip: { bet in
copyBet(bet)
},
onBetShareHandler: { completion in
Task {
let bets = await getBets()
completion(bets)
}
},
onClose: {
// Handle close
}
)Customize sport icons displayed throughout the Central Hub for different sports like soccer, basketball, etc.
SportIconSettings allows you to override the default sport icons with your own custom images. This is particularly useful when you want to match your app's design system or provide higher quality sport-specific imagery.
Use Cases:
Sport Icons appear in:
SportIconSettings Properties:
sportIconHandler ((String?) -> Image)? optional
A closure that receives a sport ID string and returns the corresponding Image. The sport ID is typically a string representation of the Sportradar sport identifier. You must provide an image for all sport IDs, including a default fallback for unknown sports.
Parameters:
sportId: Optional string representing the sport (e.g., "1" for soccer, "2" for basketball)Returns: Image to display for the given sport. Always provide a default image for unknown sport IDs.
Sport Icon Customization:
import SwiftUI
import VirtualStadiumUISDK
// Example: Custom sport icons
let customSportIcons = SportIconSettings { sportId in
switch sportId {
case "1": // Soccer
return Image("custom_soccer_icon")
case "2": // Basketball
return Image("custom_basketball_icon")
default:
return Image("default_sport_icon") // Default fallback
}
}
// Use in CentralHubSettings
let settings = CentralHubSettings(
oddsType: .eu,
themeSettings: CentralHubThemeSettings(),
sportIcons: customSportIcons,
fontSettings: FontSettings(),
translationSettings: CentralHubTranslationSettings()
)
// Apply to CentralHubView
CentralHubView(
userJWT: jwt,
userId: userId,
centralHubSettings: settings,
onCopyToBetslip: { bet in
copyBet(bet)
},
onBetShareHandler: { completion in
Task {
let bets = await getBets()
completion(bets)
}
},
onClose: {
// Handle close
}
)Common Sport IDs:
// Sportradar Sport IDs (examples)
// "1" = Soccer
// "2" = Basketball
let sportIcons = SportIconSettings { sportId in
switch sportId {
case "1":
return Image("soccer_icon")
case "2":
return Image("basketball_icon")
default:
return Image("generic_sport_icon") // Always provide default
}
}Use your app's custom fonts throughout the Central Hub:
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
}Custom Fonts Example:
let customFonts = FontSettings(
fontRegular: UIFont(name: "YourFont-Regular", size: 16),
fontMedium: UIFont(name: "YourFont-Medium", size: 16),
fontBold: UIFont(name: "YourFont-Bold", size: 16),
inputFieldPlaceholderFont: UIFont(name: "YourFont-Regular", size: 17)
)
let settings = CentralHubSettings(
fontSettings: customFonts
)Customize all text strings displayed in the Central Hub for complete localization control:
public struct CentralHubTranslationSettings {
public var navigationTitles: NavigationTitleTranslations
public var onboarding: OnboardingTranslations
public var segmentedSectionTabTranslations: SegmentedSectionTranslations
public var profileHub: ProfileHubTranslations
public var otherUserProfileHub: OtherUserProfileHubTranslations
public var betsHub: BetsHubTranslations
public var sortingSheet: SortingSheetTranslations
public var editProfile: EditProfileTranslations
public var followersHub: FollowersHubTranslations
public var commentingHub: CommentingHubTranslations
public var shareBetHub: ShareBetHubTranslations
public var betshareNotificationsHub: BetshareNotificationHubTranslations
public var socialNotificationsHub: SocialNotificationHubTranslations
public var userSearch: UserSearchTranslations
public var userHeaderNotificationDescription: UserHeaderNotificationDescriptionTranslation
public var betView: BetViewTranslations
public var general: GeneralTranslations
}Custom Translations Example:
let spanishTranslations = CentralHubTranslationSettings(
navigationTitles: NavigationTitleTranslations(
profile: "Perfil",
bets: "Apuestas",
search: "Buscar",
followers: "Seguidores",
following: "Siguiendo"
),
betsHub: BetsHubTranslations(
shareBetslipButtonTitle: "Compartir Apuesta",
createdFilterSectionTitle: "ÚLTIMAS APUESTAS",
copyCountFilterSectionTitle: "MÁS COPIADAS",
commentCountFilterSectionTitle: "MÁS COMENTADAS",
emptyContent: "Sin apuestas compartidas"
),
general: GeneralTranslations(
follow: "Seguir",
unfollow: "Dejar de seguir",
copyBetslip: "COPIAR APUESTA",
bets: "Apuestas",
copied: "Copiado"
)
)
let settings = CentralHubSettings(
translationSettings: spanishTranslations
)To enable bet sharing, provide the onBetShareHandler callback that returns your user's bet slips. The Central Hub will display these bets in a selection screen where users can choose which bet to share.
The BetPayload model represents a bet slip that can be shared and copied. Ensure your bet data is properly mapped to this structure.
Key Requirements:
The same BetPayload structure is used for both sharing bets and copying bets via the onCopyToBetslip callback.
Bet Sharing Implementation:
import SwiftUI
import VirtualStadiumUISDK
struct CentralHubContainer: View {
let jwt: String
let userId: String
@State private var showToast = false
@State private var toastMessage = ""
var body: some View {
CentralHubView(
userJWT: jwt,
userId: userId,
centralHubSettings: CentralHubSettings(),
// Handle bet copying
onCopyToBetslip: { copiedBet in
// Add the copied bet to user's bet slip
addToBetSlip(copiedBet)
// Show confirmation
toastMessage = "Bet copied to slip!"
showToast = true
},
// Provide user's bet slips for sharing
onBetShareHandler: { completion in
Task {
/**
* TODO(developer): Fetch from your backend
*/
// Fetch user's current bet slips
let myBets = await fetchUserBetSlips()
// Map to BetPayload format
let betPayloads = myBets.map { bet in
BetPayload(
id: bet.id,
betSlipId: bet.slipId,
betType: bet.type,
currency: bet.currency,
combinedOdds: Odds(
decimalValue: Float(bet.totalOdds),
displayValue: "\(bet.totalOdds)"
),
stake: Stake(
value: bet.stake,
currencyCode: bet.currency
),
payout: Payout(
value: bet.potentialWin,
currencyCode: bet.currency
),
cashOut: nil,
bets: bet.selections.map { selection in
Bet(
id: selection.id,
betType: selection.type,
event: BetEvent(
id: selection.eventId,
name: selection.eventName,
teams: selection.teams
),
markets: [
BetMarket(
id: selection.marketId,
name: selection.marketName,
outcomes: [
Outcome(
id: selection.outcomeId,
name: selection.outcomeName,
odds: Odds(
decimalValue: Float(selection.odds),
displayValue: "\(selection.odds)"
)
)
]
)
],
odds: Odds(
decimalValue: Float(selection.odds),
displayValue: "\(selection.odds)"
)
)
}
)
}
completion(betPayloads)
}
},
onClose: {
// Handle close
}
)
.toast(isPresented: $showToast, message: toastMessage)
}
private func fetchUserBetSlips() async -> [MyBet] {
/**
* TODO(developer): Implement your bet fetching logic
*/
// Example: return await apiClient.fetchUserBetSlips()
return []
}
private func addToBetSlip(_ bet: BetPayload) {
/**
* TODO(developer): Implement bet slip logic
*/
print("Adding bet to slip: \(bet.id)")
// Example: betSlipManager.addBet(bet)
}
}Complete Central Hub Integration Example:
import SwiftUI
import VirtualStadiumUISDK
import Combine
class CentralHubViewModel: ObservableObject {
@Published var jwtToken: String
@Published var userId: String
@Published var isLoading: Bool = true
init(jwtToken: String, userId: String) {
self.jwtToken = jwtToken
self.userId = userId
}
func fetchUserBetSlips() -> [BetPayload] {
/**
* TODO(developer): Fetch bets and convert them to BetPayload array. The example here converts a mock bet from JSON to BetPayload
*/
let jsonString = """
{
"id": "sr:bet-share-event:12345678901",
"betSlipId": "some:123",
"betType": "single",
"currency": "€",
"combinedOdds": {
"decimalValue": 5.67
},
"stake": {
"value": "25,00"
},
"payout": {
"value": "37.50"
},
"bets": [
{
"id": "x",
"betType": "single",
"event": {
"id": "x1",
"name": "Manchester United VS Chelsea",
"sportId": "1"
},
"markets": [
{
"id": "a",
"name": "1 x 2",
"outcomes": [
{
"id": "a1",
"name": "Manchester United"
}
]
}
]
}
]
}
"""
return [try! JSONDecoder().decode(BetPayload.self, from: jsonString.data(using: .utf8)!)]
}
func addBetToSlip(_ bet: BetPayload) {
/**
* TODO(developer): Implement copy bet slip logic
*/
print("Adding bet: \(bet.id)")
}
func loadSDK() async {
Task {
do {
try await VirtualStadiumUISDK_STGDEV.Managers.SDK.shared.initialize()
self.isLoading = false
} catch {
print("Error initializing SDK: \(error)")
}
}
}
}
struct CentralHubViewContainer: View {
@StateObject private var viewModel: CentralHubViewModel
init() {
_viewModel = StateObject(
wrappedValue: CentralHubViewModel(
jwtToken: "Pre generated JWT",
userId: "userId"
)
)
}
var body: some View {
VStack {
switch viewModel.isLoading {
case false:
CentralHubView(
userJWT: viewModel.jwtToken,
userId: viewModel.userId,
centralHubSettings: createCustomSettings(),
onCopyToBetslip: { bet in
viewModel.addBetToSlip(bet)
},
onBetShareHandler: { completion in
let bets = viewModel.fetchUserBetSlips()
completion(bets)
},
onClose: {
// Cose central hub
}
)
case true:
ProgressView()
}
}
.task {
await viewModel.loadSDK()
}
}
private func createCustomSettings() -> CentralHubSettings {
// Custom theme
let customTheme = CentralHubThemeSettings(
background: Color(red: 0.95, green: 0.95, blue: 0.97),
toastColors: ToastColors(
successColors: SuccessToastColors(
backgroundColor: .green,
textColor: .white
)
),
buttonColors: CentralHubButtonColors(
followButtonColors: FollowButtonColors(
background: Color.blue.opacity(0.15),
text: .blue
),
shareBetslipButtonColors: ShareBetslipButtonColors(
backgroundColor: .blue,
textColor: .white
)
),
navigationBarColors: CentralHubNavigationBarColors(
background: .white,
title: .black,
icons: .black
)
)
// Custom sport icons
let customSportIcons = SportIconSettings { sportId in
switch sportId {
case "1": // Soccer
return Image("custom_soccer_icon")
case "2": // Basketball
return Image("custom_basketball_icon")
default:
return Image("generic_sport_icon")
}
}
// Custom fonts
let customFonts = FontSettings(
fontRegular: UIFont(name: "CustomFont-Regular", size: 16),
fontMedium: UIFont(name: "CustomFont-Medium", size: 16),
fontBold: UIFont(name: "CustomFont-Bold", size: 16)
)
return CentralHubSettings(
oddsType: .us,
themeSettings: customTheme,
sportIcons: customSportIcons,
fontSettings: customFonts,
translationSettings: CentralHubTranslationSettings()
)
}
}
struct MainAppView: View {
var body: some View {
CentralHubViewContainer()
}
}
// Preview
struct CentralHubScreen_Previews: PreviewProvider {
static var previews: some View {
CentralHubViewContainer()
}
}