Follow entities - iOS
The Follow entities feature allows viewers to follow and unfollow entities (players, teams, properties/competitions) directly within the Blaze SDK player experience. This feature provides a seamless way to track viewer preferences and to integrate with your app's follow system.
Table of contents
- Architecture
- Integration
- Customization
- Data models
- Best practices
- Example: Complete integration
- Troubleshooting
- Support
Architecture
The Follow Entities system consists of several key components:
BlazeFollowEntitiesManager: The main instance for managing follow stateBlazeFollowEntity: Model representing a followable entityBlazeFollowEntityType: Enum defining entity types with fallback supportBlazeMomentsPlayerFollowEntityStyle: Style configuration for the follow UI componentBlazeFollowEntitiesDelegate: Delegate for handling user interactions
Integration
Basic setup
Access the follow entities manager through the Blaze SDK singleton:
import BlazeSDK
let followEntitiesManager = Blaze.shared.followEntitiesManagerSetting initial follow state
Before presenting the player, set the initial followed entities to ensure correct UI state:
// Create follow entities from your app's data
let followedEntities: Set<BlazeFollowEntity> = [
BlazeFollowEntity(id: "player_123"),
BlazeFollowEntity(id: "team_456"),
BlazeFollowEntity(id: "league_789")
]
// Get base player style
var momentsPlayerStyle = BlazeMomentsPlayerStyle.base()
// Enable follow entity component (by default it's not visible)
momentsPlayerStyle.followEntity.isVisible = true
// Set the followed entities (replacing any existing entities)
followEntitiesManager.setFollowedEntities(followedEntities)
// Run player
Blaze.shared.playMoments(
dataSourceType: .yourDataSource,
style: momentsPlayerStyle,
triggerSource: .entryPoint
)Important: Always set the initial follow state before opening the player to ensure the UI reflects the correct state from the start.
Handling user interactions
Implement the BlazeFollowEntitiesDelegate to receive callbacks when users interact with follow buttons:
class YourViewController: UIViewController, BlazeFollowEntitiesDelegate {
override func viewDidLoad() {
super.viewDidLoad()
// Set delegate
Blaze.shared.followEntitiesManager.delegate = self
}
func onFollowEntityClicked(_ params: BlazeFollowEntityClickedParams) {
let entityId = params.followEntity.id
let isNowFollowing = params.newFollowingState
let contentId = params.sourceId
let playerType = params.playerType
if isNowFollowing {
// User just followed the entity
print("User followed entity: \(entityId) from content: \(contentId)")
// Sync with your backend
yourBackendService.followEntity(entityId)
} else {
// User just unfollowed the entity
print("User unfollowed entity: \(entityId) from content: \(contentId)")
// Sync with your backend
yourBackendService.unfollowEntity(entityId)
}
}
}Note: The internal follow state is automatically updated before the delegate is called, so
newFollowingStatereflects the state after the user's action.
Managing follow state
Adding followed entities
Add new entities to the existing followed set without replacing all entities:
let newEntities: Set<BlazeFollowEntity> = [
BlazeFollowEntity(id: "player_999")
]
followEntitiesManager.insertFollowedEntities(newEntities)Removing followed entities
Remove specific entities from the followed set:
let entitiesToRemove: Set<BlazeFollowEntity> = [
BlazeFollowEntity(id: "player_123")
]
followEntitiesManager.removeFollowedEntities(entitiesToRemove)Getting current follow state
Retrieve all currently followed entities:
let currentlyFollowed = followEntitiesManager.getFollowedEntities()
for entity in currentlyFollowed {
print("Entity ID: \(entity.id)")
}Customization
Player style configuration
Enable or disable the follow entity UI component via the player style:
// Get base player style
var momentsPlayerStyle = BlazeMomentsPlayerStyle.base()
// Enable follow entity component
momentsPlayerStyle.followEntity.isVisible = trueEntity type priority
The BlazeFollowEntityType enum provides a powerful mechanism for entity type resolution with fallback chains. When content has multiple followable entities (e.g., a goal moment might have both a player and a team), the SDK uses this type to determine which entity to display in the UI.
How fallback works:
The SDK attempts to find an entity matching your preferred type. If not found, it follows the fallback chain until a match is found or the chain ends.
Usage examples:
// Get followEntity from the base player style
var followEntity = BlazeMomentsPlayerStyle.base().followEntity
// Example 1: Player only (no fallback)
// Will show player entity, or hide component if no player available
followEntity.entityType = .player(fallbackType: nil)
// Example 2: Player with team fallback
// Will show player if available, otherwise team, otherwise hide component
followEntity.entityType = .player(fallbackType: .team(fallbackType: nil))
// Example 3: Complex fallback chain
// Priority: Player → Team → Property → Any available entity
followEntity.entityType = .player(
fallbackType: .team(
fallbackType: .property(
fallbackType: .firstAvailable
)
)
)
// Example 4: Team with "any" fallback
// Will show team if available, otherwise any available entity type
followEntity.entityType = .team(fallbackType: .firstAvailable)
// Example 5: Accept any available entity (no preference)
// Will show the first entity returned from backend
followEntity.entityType = .firstAvailableVisual customization
Customize the appearance of the follow button for both follow and unfollow states:
var momentsPlayerStyle = BlazeMomentsPlayerStyle.base()
// Customize the FOLLOWED state
momentsPlayerStyle.followEntity.followState.avatar.borderWidth = 2.0
momentsPlayerStyle.followEntity.followState.avatar.borderColor = .blue
momentsPlayerStyle.followEntity.followState.chip.backgroundColor = .green
momentsPlayerStyle.followEntity.followState.chip.iconColor = .black
// Customize the UNFOLLOWED state
momentsPlayerStyle.followEntity.unfollowState.avatar.borderWidth = 1.5
momentsPlayerStyle.followEntity.unfollowState.avatar.borderColor = .white
momentsPlayerStyle.followEntity.unfollowState.chip.backgroundColor = .white
momentsPlayerStyle.followEntity.unfollowState.chip.iconColor = .black
momentsPlayerStyle.followEntity.unfollowState.chip.contentSource = .text // Show "Follow" text labelContent source options
The chip can display different content based on your design requirements:
.icon— Displays an icon (compact representation).text— Displays "Follow" text (only effective in unfollowed state)
Note: When in the followed state, the
.textcontent source will automatically be treated as.iconto maintain consistent visual representation.
Data models
BlazeFollowEntity
Represents a followable entity with a unique identifier.
public struct BlazeFollowEntity: Equatable, Hashable {
public let id: String
public init(id: String)
}BlazeFollowEntityType
Defines the type of entity with hierarchical fallback support.
public enum BlazeFollowEntityType: Equatable, Hashable {
case firstAvailable
case player(fallbackType: BlazeFollowEntityType?)
case team(fallbackType: BlazeFollowEntityType?)
case property(fallbackType: BlazeFollowEntityType?)
}BlazeFollowEntityClickedParams
Encapsulates information about a follow entity click event.
public struct BlazeFollowEntityClickedParams {
public let sourceId: String // Content ID where click occurred
public let playerType: BlazePlayerType // Player type (.moments, .stories, .videos)
public let newFollowingState: Bool // State after the click
public let followEntity: BlazeFollowEntity // The clicked entity
}BlazeMomentsPlayerFollowEntityStyle
Complete style configuration for the follow entity component.
public struct BlazeMomentsPlayerFollowEntityStyle {
public var isVisible: Bool
public var followState: BlazeMomentsPlayerFollowEntityStateStyle
public var unfollowState: BlazeMomentsPlayerFollowEntityStateStyle
public var entityType: BlazeFollowEntityType
}Best practices
1. Initialize follow state early
Always set the initial follow state before opening the player:
// ✅ Good
followEntitiesManager.setFollowedEntities(yourFollowedEntities)
Blaze.shared.playMoments(...)
// ❌ Bad - UI might show incorrect state initially
Blaze.shared.playMoments(...)
followEntitiesManager.setFollowedEntities(yourFollowedEntities) // Too late!2. Persist follow state
Save follow state changes to persistent storage in the delegate callback:
func onFollowEntityClicked(_ params: BlazeFollowEntityClickedParams) {
// Update local persistence (if needed)
UserDefaults.standard.set(params.newFollowingState, forKey: "follow_\(params.followEntity.id)")
// Sync with your backend
Task {
if params.newFollowingState {
// User followed the entity - notify your backend
await yourBackendService.followEntity(
entityId: params.followEntity.id,
contentId: params.sourceId
)
} else {
// User unfollowed the entity - notify your backend
await yourBackendService.unfollowEntity(
entityId: params.followEntity.id,
contentId: params.sourceId
)
}
}
}3. Handle backend sync failures to revert the follow state in the SDK
Implement error handling for backend synchronization:
func onFollowEntityClicked(_ params: BlazeFollowEntityClickedParams) {
Task {
do {
if params.newFollowingState {
try await backendService.followEntity(params.followEntity.id)
} else {
try await backendService.unfollowEntity(params.followEntity.id)
}
} catch {
// Revert the follow state in the SDK
if params.newFollowingState {
followManager.removeFollowedEntities([params.followEntity])
} else {
followManager.insertFollowedEntities([params.followEntity])
}
// Show error to user
showErrorAlert("Failed to update follow state")
}
}
}4. Use entity type priority wisely
Configure entity type priority based on your content:
// For sports content focused on individual athletes
momentsPlayerStyle.followEntity.entityType = .player(
fallbackType: .team(fallbackType: nil)
)
// For team sports content
momentsPlayerStyle.followEntity.entityType = .team(
fallbackType: .player(fallbackType: nil)
)
// For league/competition content
momentsPlayerStyle.followEntity.entityType = .property(
fallbackType: .firstAvailable
)5. Clean up delegate
Remove the delegate reference when your instance that used it is deallocated:
deinit {
Blaze.shared.followEntitiesManager.delegate = nil
}Example: Complete integration
Here's a complete example showing best practices for integrating the follow entity feature:
import UIKit
import BlazeSDK
class YourMomentsViewController: UIViewController {
private let backendService = YourBackendService()
private var followManager: BlazeFollowEntitiesManager {
Blaze.shared.followEntitiesManager
}
override func viewDidLoad() {
super.viewDidLoad()
// Set delegate
followManager.delegate = self
// Load and set initial follow state
Task {
await loadInitialFollowState()
}
}
deinit {
followManager.delegate = nil
}
// MARK: - Setup
private func loadInitialFollowState() async {
do {
// Fetch from backend
let backendEntities = try await backendService.fetchFollowedEntities()
// Convert to BlazeFollowEntity
let blazeEntities: Set<BlazeFollowEntity> = Set(
backendEntities.map { BlazeFollowEntity(id: $0.id) }
)
// Set in SDK
followManager.setFollowedEntities(blazeEntities)
// Now safe to open player
openPlayer()
} catch {
// Still open player with empty follow state
followManager.setFollowedEntities([])
openPlayer()
}
}
private func openPlayer() {
// Create and customize player style
var style = BlazeMomentsPlayerStylePresets.base()
// Enable follow entity
style.followEntity.isVisible = true
style.followEntity.entityType = .player(
fallbackType: .team(fallbackType: .firstAvailable)
)
// Customize colors
style.followEntity.followState.chip.backgroundColor = UIColor(hexString: "#FF00B27C")!
style.followEntity.unfollowState.chip.contentSource = .text
// Play moments
Blaze.shared.playMoments(
dataSourceType: .yourDataSource,
entryContentId: "moment_123",
style: style,
triggerSource: .entryPoint
)
}
}
// MARK: - BlazeFollowEntitiesDelegate
extension MomentsViewController: BlazeFollowEntitiesDelegate {
func onFollowEntityClicked(_ params: BlazeFollowEntityClickedParams) {
let entityId = params.followEntity.id
let isFollowing = params.newFollowingState
// Log analytics
logFollowEvent(
entityId: entityId,
action: isFollowing ? "follow" : "unfollow",
sourceId: params.sourceId,
playerType: params.playerType
)
// Sync with backend
Task {
do {
if isFollowing {
try await backendService.followEntity(entityId)
} else {
try await backendService.unfollowEntity(entityId)
}
} catch {
// Revert SDK state on failure
if isFollowing {
followManager.removeFollowedEntities([params.followEntity])
} else {
followManager.insertFollowedEntities([params.followEntity])
}
// Show error
await showError("Failed to update follow state. Please try again.")
}
}
}
private func showError(_ message: String) async {
await MainActor.run {
let alert = UIAlertController(title: "Error", message: message, preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default))
present(alert, animated: true)
}
}
}Troubleshooting
Follow button not appearing
- Ensure
followEntity.isVisible = truein your player style - Verify that the content has follow entities in the backend response
- Check that the entity type priority matches available entities
Wrong follow state displayed
- Set initial follow state before opening the player
- Verify entity IDs match between your system and backend
Delegate not being called
- Ensure delegate is set before opening the player
- Verify delegate is not nil
- Check that the delegate conforms to
BlazeFollowEntitiesDelegate
Support
For additional support or questions about the Follow entity feature, please contact the Blaze SDK team or refer to the main SDK documentation.
Updated 11 days ago
