BlazeSwiftUIVideosInlinePlayerView
BlazeSwiftUIVideosInlinePlayerView is a SwiftUI View that provides a native SwiftUI interface for inline video playback with full control over player lifecycle and state. It works in conjunction with BlazeSwiftUIVideoInlinePlayerController for programmatic control over playback. The controller's lifecycle determines the player's lifecycle, enabling two usage patterns for different scenarios.
Give each instance a unique, stable containerIdentifier for proper analytics tracking and player management. Updating any Configuration property recreates the player instance, which can be expensive, so use with care.
Player modes
Two player modes are available:
Interactive mode - a fully interactive inline player with comprehensive overlay controls including play/pause, seek bar, mute, share, like, and navigation buttons.
Preview mode - a minimal overlay player with only essential controls (mute, replay) that transitions to full screen on tap.
Usage patterns
Approach 1: simple screens (controller created with view)
For simple, standalone video players where the player lifecycle should match the view lifecycle. Ideal for dedicated video screens, detail views, or standalone video components. Interactive interface is very suitable for this use case.
struct VideoDetailView: View {
@StateObject private var playerController = BlazeSwiftUIVideoInlinePlayerController()
@State private var embeddedState: EmbeddedState = .placeholder
var body: some View {
VStack {
Text("Video Title")
.font(.title)
BlazeSwiftUIVideosInlinePlayerView(
configuration: Configuration(
playerMode: .interactive(interactivePlyerStyle: .base()),
dataSourceType: .labels(.singleLabel("highlights")),
containerIdentifier: "video-detail-player"
),
playerController: playerController,
embeddedState: embeddedState
)
.aspectRatio(16/9, contentMode: .fit)
.inlinePlayerDelegate(BlazeInlinePlayerDelegate(
onPlayerDidAppear: { playerType, containerId in
print("Player appeared in detail view")
}
))
HStack {
Button("Show Player") {
embeddedState = .player(autoPlayOnStart: false)
}
Button("Play") {
playerController.resumePlayer()
}
Button("Full Screen") {
playerController.enterFullScreen()
}
}
}
.padding()
}
}Approach 2: feed views (controller stored in model or high-level container)
For video feeds and lists where players need to outlive individual view cells for better performance and state preservation during scrolling. Preview mode works well here, out of the box.
// Feed ViewModel with items containing controllers
class VideosFeedViewModel: ObservableObject {
enum FeedItem: Identifiable {
case inlinePreview(InlineFeedItem)
case inlineInteractive(InlineFeedItem)
var id: String {
switch self {
case .inlinePreview(let item), .inlineInteractive(let item):
item.id
}
}
}
struct InlineFeedItem: Identifiable, Equatable {
let id: String
let title: String
let playerMode: BlazeVideosInlinePlayer.PlayerMode
let dataSourceType: BlazeDataSourceType
let inlinePlayerDelegate: BlazeInlinePlayerDelegate
let playerController: BlazeSwiftUIVideoInlinePlayerController
static func == (lhs: InlineFeedItem, rhs: InlineFeedItem) -> Bool {
lhs.id == rhs.id && lhs.title == rhs.title
}
}
@Published var feedItems: [FeedItem] = []
@Published var currentlyPlayingItemId: String?
private func createFeedItem(id: String, title: String, label: String) -> InlineFeedItem {
InlineFeedItem(
id: id,
title: title,
playerMode: .preview(previewPlayerStyle: .base()),
dataSourceType: .labels(.singleLabel(label)),
inlinePlayerDelegate: BlazeInlinePlayerDelegate(
onPlayerDidAppear: { playerType, containerId in
print("Player appeared: \(containerId)")
}
),
playerController: BlazeSwiftUIVideoInlinePlayerController()
)
}
}
// Feed cell view
struct VideoFeedCell: View {
let feedItem: VideosFeedViewModel.InlineFeedItem
let embeddedState: EmbeddedState
private var player: BlazeSwiftUIVideoInlinePlayerController { feedItem.playerController }
var body: some View {
VStack(spacing: 0) {
// Header
HStack {
Text(feedItem.title)
.font(.headline)
Spacer()
}
.padding()
.background(Color(.systemGray6))
// Player using controller from model (Approach #2)
BlazeSwiftUIVideosInlinePlayerView(
configuration: Configuration(
playerMode: feedItem.playerMode,
dataSourceType: feedItem.dataSourceType,
containerIdentifier: feedItem.id
),
playerController: player,
embeddedState: embeddedState
)
.inlinePlayerDelegate(feedItem.inlinePlayerDelegate)
.aspectRatio(16/9, contentMode: .fit)
.background(Color.black)
}
.background(Color(.systemBackground))
.cornerRadius(12)
}
}Initialization
Configuration-based initializer
public init(
configuration: Configuration,
playerController: BlazeSwiftUIVideoInlinePlayerController,
embeddedState: EmbeddedState = .placeholder
)Creates a new BlazeSwiftUIVideosInlinePlayerView with the specified configuration object.
Parameters:
configuration- a configuration object containing all the destructible player settings that trigger player recreation when changed.playerController- the controller that manages the player's lifecycle. The underlying player is tied to this controller's lifecycle.embeddedState- the initial state of the player. Defaults to.placeholderto show a thumbnail; use.player(autoPlayOnStart:)to immediately embed the video player.
@StateObject private var playerController = BlazeSwiftUIVideoInlinePlayerController()
let config = BlazeSwiftUIVideosInlinePlayerView.Configuration(
playerMode: .preview(previewPlayerStyle: .base()),
dataSourceType: .labels(.singleLabel("highlights")),
containerIdentifier: "feed-item-\(item.id)"
)
BlazeSwiftUIVideosInlinePlayerView(
configuration: config,
playerController: playerController,
embeddedState: .placeholder
)
.inlinePlayerDelegate(myDelegate)Configuration
Configuration structure
struct BlazeSwiftUIVideosInlinePlayerView.ConfigurationThe configuration struct containing all properties that trigger player recreation when changed. These are "destructible" properties: changing any of them recreates the underlying player instance to ensure proper behavior and avoid state inconsistencies.
Updating any of these properties recreates the player instance, which can be expensive, so use with care.
Properties:
playerMode: BlazeVideosInlinePlayer.PlayerMode- The visual and behavioral style of the player containerdataSourceType: BlazeDataSourceType- The data source configuration that defines how video content is retrievedcontainerIdentifier: String- Unique identifier for this player instanceshouldOrderVideosByReadStatus: Bool- Whether videos should be ordered based on user's viewing history (default: true)cachePolicyLevel: BlazeCachePolicyLevel?- Cache policy configuration for optimizing video loading behavior (default: nil)adsConfigType: BlazeVideosAdsConfigType- Advertisement configuration (default: .firstAvailableAdsConfig)
let configuration = BlazeSwiftUIVideosInlinePlayerView.Configuration(
playerMode: .interactive(interactivePlyerStyle: .base()),
dataSourceType: .labels(.singleLabel("NBA")),
containerIdentifier: "main-feed-player",
shouldOrderVideosByReadStatus: true,
cachePolicyLevel: nil,
adsConfigType: .firstAvailableAdsConfig
)EmbeddedState
enum BlazeSwiftUIVideosInlinePlayerView.EmbeddedStateThe embedding states of the video player, allowing dynamic control over whether to display a placeholder thumbnail or an active video player. This enum provides fine-grained control over player lifecycle, enabling scenarios such as feed-based video players where placeholder images are shown until the user scrolls to a specific position or explicitly requests playback.
In this SwiftUI view the player's embedding states are toggled by reactive changes propagated from the parent view's body. Set up your container so that it triggers embedded-state changes when needed.
.placeholder
Shows only a thumbnail image that users can tap to start playback.
In this state, the view shows a static thumbnail image representing the video content. This is optimal for performance in scenarios like feed views where multiple video players would otherwise consume excessive resources.
// Show placeholder initially
embeddedState = .placeholder.player(autoPlayOnStart: Bool)
Immediately embeds the video player with the option to automatically start playback.
Parameters:
autoPlayOnStart: whether the video should begin playing automatically when the player is embedded. Set totruefor immediate playback orfalseto wait for user interaction.
// Player that waits for user interaction
embeddedState = .player(autoPlayOnStart: false)
// Player that starts immediately
embeddedState = .player(autoPlayOnStart: true)View modifiers
inlinePlayerDelegate
func inlinePlayerDelegate(_ delegate: BlazeInlinePlayerDelegate?) -> BlazeSwiftUIVideosInlinePlayerViewSets the delegate for handling player events and user interactions.
Parameters:
delegate: the delegate instance for handling various player events.
let delegate = BlazeInlinePlayerDelegate(
onPlayerDidAppear: { playerType, containerId in
print("Player appeared: \(containerId)")
},
onPlayerDidEnterFullScreen: { sourceId, playerType in
print("Player entered full screen")
},
onPlaceholderClicked: { sourceId, playerType in
print("Placeholder was clicked")
}
)
BlazeSwiftUIVideosInlinePlayerView(...)
.inlinePlayerDelegate(delegate)Managing embedded state
Reactive state changes
Changing the embedded state of the BlazeSwiftUIVideosInlinePlayerView relies on the embedded state value passed in its initializer, making it respond to changes reactively depending on internal logic of your custom parent view.
struct ChangingEmbeddedStateExample: View {
@State private var embeddedState: EmbeddedState = .placeholder
var body: some View { // Will run with any internal state change
VStack {
// Player with dynamic embedded state
BlazeSwiftUIVideosInlinePlayerView(
configuration: Configuration(
playerMode: .interactive(interactivePlyerStyle: .base()),
dataSourceType: .labels(.singleLabel("highlights")),
containerIdentifier: "simple-player"
),
playerController: playerController,
embeddedState: embeddedState // This updates when state changes
)
.frame(height: 240)
// Button to toggle between placeholder and player
Button(embeddedState == .placeholder ? "Load Player" : "Show Thumbnail") {
// Toggle between states
if case .placeholder = embeddedState {
embeddedState = .player(autoPlayOnStart: true)
} else {
embeddedState = .placeholder
}
}
.padding()
.background(Color.blue)
.foregroundColor(.white)
.cornerRadius(8)
}
.padding()
}
}Videos feed example
Here's a simple example showing how to manage multiple players in a feed where only the current item has an active player:
struct VideosFeedView: View {
@State private var currentPlayingId: String? = nil
let videoItems = ["video-1", "video-2", "video-3", "video-4"]
var body: some View {
ScrollView {
LazyVStack(spacing: 16) {
ForEach(videoItems.indices, id: \.self) { index in
// Player view that changes state based on current index
BlazeSwiftUIVideosInlinePlayerView(
configuration: Configuration(
playerMode: .preview(previewPlayerStyle: .base()),
dataSourceType: .labels(.singleLabel("highlights")),
containerIdentifier: "feed-\(videoItems[index])"
),
playerController: playerController,
// Only the current index is a player, others are placeholders
embeddedState: currentPlayingId == videoItems[index]
? .player(autoPlayOnStart: true)
: .placeholder
)
.frame(height: 240)
.onAppear {
// Perform your own logic of selecting the current index
currentPlayingId = videoItems[index]
}
.padding(.vertical, 8)
}
}
.padding()
}
}
}Best practices
Performance optimization
- Use
.placeholderembedded state initially for better performance in feeds - Store controllers in model objects for feed scenarios to avoid recreation during scrolling
- Minimize
Configurationchanges as they trigger expensive player recreation
State management
- Always use stable, unique
containerIdentifiervalues for proper analytics and management - Group
Configurationproperties together and avoid frequent changes - Use
embeddedStatefor dynamic control without triggering player recreation - Consider using computed properties for conditional embedded states
Lifecycle management
- Use
@StateObjectfor controllers in SwiftUI views to ensure proper lifecycle - Store controllers in data models for feed items that outlive view cells
User experience
- Consider auto-play policies and user preferences
- Implement proper loading states and error handling through delegates
- Test performance with multiple players in feed scenarios
Updated about 2 months ago
