GuidesAPI ReferenceRelease Notes
HomeLog InHome
Guides

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

The Follow Entities system consists of several key components:

  • BlazeFollowEntitiesManager: The main instance for managing follow state
  • BlazeFollowEntity: Model representing a followable entity
  • BlazeFollowEntityType: Enum defining entity types with fallback support
  • BlazeMomentsPlayerFollowEntityStyle: Style configuration for the follow UI component
  • BlazeFollowEntitiesDelegate: Delegate for handling user interactions

Integration

Basic setup

Access the follow entities manager through the Blaze SDK singleton:

import BlazeSDK

let followEntitiesManager = Blaze.shared.followEntitiesManager

Setting 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 newFollowingState reflects 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 = true

Entity 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 = .firstAvailable

Visual 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 label

Content 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 .text content source will automatically be treated as .icon to 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 = true in 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.


Did this page help you?